-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsearch.xml
6169 lines (5792 loc) · 968 KB
/
search.xml
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>OpenCV课程资料</title>
<url>/65de593f/</url>
<content><![CDATA[<p>这里是我OpenCV课程的相关资料,后面还会不断补充…</p>
<span id="more"></span>
<h2 id="Windows下编译OpenCV"><a href="#Windows下编译OpenCV" class="headerlink" title="Windows下编译OpenCV"></a>Windows下编译OpenCV</h2><h3 id="下载必要的工具和库"><a href="#下载必要的工具和库" class="headerlink" title="下载必要的工具和库"></a>下载必要的工具和库</h3><ol>
<li>安装python3和numpy库,下载地址:<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe</span><br></pre></td></tr></table></figure></li>
<li>安装 VS, 社区版即可,下载地址:<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://visualstudio.microsoft.com/zh-hans/downloads/</span><br></pre></td></tr></table></figure></li>
<li>安装 cmake,下载地址: <figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://github.com/Kitware/CMake/releases/download/v3.20.0-rc1/cmake-3.20.0-rc1-windows-x86_64.msi</span><br></pre></td></tr></table></figure></li>
<li>下载IPPICV<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://github.com/opencv/opencv_3rdparty.git</span><br></pre></td></tr></table></figure></li>
</ol>
<h3 id="下载opencv源码,下载地址:"><a href="#下载opencv源码,下载地址:" class="headerlink" title="下载opencv源码,下载地址:"></a>下载opencv源码,下载地址:</h3><ul>
<li>opencv源码地址: <figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git clone https://github.com/opencv/opencv.git</span><br></pre></td></tr></table></figure></li>
<li>opencv-contrib源码地址git: <figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">clone https://github.com/opencv/opencv_contrib.git</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="编译方法:"><a href="#编译方法:" class="headerlink" title="编译方法:"></a>编译方法:</h3><ul>
<li>在存放opencv源码目录中创建build目录</li>
<li>运行cmake</li>
<li>选择opencv源码目录</li>
<li>选择编译目录</li>
<li>选择CPU架构</li>
<li>增加opencv-contrib选项, OPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules</li>
<li>检查编译选项<ul>
<li>勾选 opencv_world</li>
</ul>
</li>
<li>生成编译脚本</li>
</ul>
<h3 id="打开opencv工程文件"><a href="#打开opencv工程文件" class="headerlink" title="打开opencv工程文件"></a>打开opencv工程文件</h3><ol>
<li>选择输出版本类型</li>
<li>进行编译</li>
</ol>
<h2 id="Ubuntu下编译OpenCV4"><a href="#Ubuntu下编译OpenCV4" class="headerlink" title="Ubuntu下编译OpenCV4"></a>Ubuntu下编译OpenCV4</h2><ul>
<li><p>安装开发工具</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ sudo apt-get install build-essential cmake unzip pkg-config</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装image and video I/O libraries</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ sudo apt-get install libjpeg-dev libpng-dev libtiff-dev</span><br><span class="line">$ sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev</span><br><span class="line">$ sudo apt-get install libxvidcore-dev libx264-dev</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装GUI</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ sudo apt-get install libgtk-3-dev</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装数据优化包</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ sudo apt-get install libatlas-base-dev gfortran</span><br></pre></td></tr></table></figure>
</li>
<li><p>安装 Python 3</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ sudo apt-get install python3-dev</span><br></pre></td></tr></table></figure></li>
</ul>
]]></content>
<categories>
<category>人工智能</category>
<category>图像处理</category>
</categories>
<tags>
<tag>图像处理</tag>
<tag>OpenCV</tag>
<tag>人工智能</tag>
</tags>
</entry>
<entry>
<title>Go的性能究竟如何?</title>
<url>/1ed22639/</url>
<content><![CDATA[<p>最近几年Go、RUST等新语言不断推陈出新,令人目不暇接。据说Go与RUST开发出的程序性能非常高,但对于我这种C/C++老鸟来说,对此并不感冒,“再快还能比C快?”这句话一直萦绕在我的心头。</p>
<p>但出于好奇,每次听到有人说Go性能多好多好时,难免都会追问一句“有没有与C进行过对比测试?”,这句并不是想“兑”谁,而是想确认一下网上的传言是否为真。</p>
<p>不幸的是,每当我问这句话时,从来没有得到一个明确答复,也搞不清是他们得出的“Go性能好”是道听途说,还是自己真实的测试结果。</p>
<p>近来时间充裕,一时兴起,心想不如做个简单的Go、C/C++、RUST的性能对比测试吧,验证一下Go的性能到底如何。</p>
<span id="more"></span>
<h2 id="简单的测试用例"><a href="#简单的测试用例" class="headerlink" title="简单的测试用例"></a>简单的测试用例</h2><p>由于我一直专注在音视频实时通信领域,因此对网络传输的性能特别关心,所以这次测试只验证一下不同语言在传输UDP包时的性能。</p>
<p>为了减少其它因素的影响,测试条件限制如下:</p>
<ul>
<li>在同一台机子上进行不同语言性能的测试,防止因硬件的不同影响测试结果</li>
<li>服务端只接收不回复,防止服务器干扰测试结果</li>
<li>客户端只发送不接收,每次只发送<code>Hello</code>几个字符</li>
<li>客户端循环发送 1000 0000次,看它的总时长是多少</li>
</ul>
<p>下面是不同语言编写的测试程序。</p>
<h3 id="C客户端代码"><a href="#C客户端代码" class="headerlink" title="C客户端代码"></a>C客户端代码</h3><p>下面是使用<code>C</code>语言实现的客户端代码的主要逻辑。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line"> gettimeofday(&tv0, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> lastime = tv0.tv_sec * <span class="number">1000000000</span> + tv0.tv_usec; </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"total: %lu, sec:%lu, nao:%u\n"</span>, lastime, tv0.tv_sec, tv0.tv_usec);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//循环 1000 0000 次</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> a=<span class="number">0</span>; a< <span class="number">10000000</span>; a++){</span><br><span class="line"> n = sendto(sock, </span><br><span class="line"> buff_send, </span><br><span class="line"> <span class="built_in">strlen</span>(buff_send), </span><br><span class="line"> <span class="number">0</span>, (<span class="keyword">struct</span> sockaddr *) &addr, </span><br><span class="line"> <span class="keyword">sizeof</span>(addr));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> gettimeofday(&tv1, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> curtime = tv1.tv_sec * <span class="number">1000000000</span> + tv1.tv_usec; </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"total:%lu, sec:%lu, nao:%u\n"</span>, curtime, tv1.tv_sec, tv1.tv_usec);</span><br><span class="line">...</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>从上述代码中可以看到,在<code>for</code>循环执行了 1000 0000 次,每次都调用 <code>sendto</code> 发送UDP数据。</p>
<h3 id="Go客户端代码"><a href="#Go客户端代码" class="headerlink" title="Go客户端代码"></a>Go客户端代码</h3><p>Go语言实现的逻辑与C客户端逻辑是类似的,代码如下:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line"> <span class="keyword">var</span> lastime <span class="type">int64</span> = time.Now().UnixNano()</span><br><span class="line"> fmt.Printf(<span class="string">"lastime: %d\n"</span>, lastime)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> a:=<span class="number">0</span>; a < <span class="number">10000000</span>; a++ {</span><br><span class="line"> <span class="comment">// 发送数据</span></span><br><span class="line"> _, err = conn.Write([]<span class="type">byte</span>(<span class="string">"Hello"</span>)) </span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> curtime <span class="type">int64</span> = time.Now().UnixNano()</span><br><span class="line"> fmt.Printf(<span class="string">"curtime: %d"</span>, curtime)</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>Go的代码非常简单,它与C的区别是其使用Write来发送数据。</p>
<h3 id="RUST客户端代码"><a href="#RUST客户端代码" class="headerlink" title="RUST客户端代码"></a>RUST客户端代码</h3><p>RUST语言实现的逻辑也是类似的,代码如下:</p>
<figure class="highlight rust"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">...</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">start</span> = Instant::<span class="title function_ invoke__">now</span>();</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">n</span> = start.<span class="title function_ invoke__">elapsed</span>().<span class="title function_ invoke__">as_nanos</span>();</span><br><span class="line"> std::<span class="built_in">println!</span>(<span class="string">" elapsed: {}"</span>, n);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//loop {</span></span><br><span class="line"> <span class="keyword">for</span> <span class="variable">_i</span> <span class="keyword">in</span> <span class="number">1</span>..<span class="number">100001</span> {</span><br><span class="line"> socket.<span class="title function_ invoke__">send_to</span>(data.<span class="title function_ invoke__">as_bytes</span>(), <span class="string">"127.0.0.1:9998"</span>)?;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> n = start.<span class="title function_ invoke__">elapsed</span>().<span class="title function_ invoke__">as_nanos</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"elapsed : {}"</span>, n);</span><br><span class="line">...</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h2 id="测试结果"><a href="#测试结果" class="headerlink" title="测试结果"></a>测试结果</h2><table>
<thead>
<tr>
<th align="left">系统</th>
<th align="left">硬件</th>
<th align="left">C</th>
<th align="left">Go</th>
<th align="left">RUST</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Mac</td>
<td align="left">2.7 GHz Intel Core i5<br>8 GB DDR3</td>
<td align="left">150秒</td>
<td align="left">85秒</td>
<td align="left">150 秒</td>
</tr>
<tr>
<td align="left">Linux</td>
<td align="left">2.7GHz Inter 8 Core i7 <br> 8G</td>
<td align="left">17-18秒</td>
<td align="left">21-22秒</td>
<td align="left">22 秒</td>
</tr>
</tbody></table>
<h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>刚看到这个结果时,我真是难以至信,Go和RUST的性能竟然与C如此接近。</p>
<p>通过上面的测试我们可以得出以下结论:</p>
<ul>
<li>新语言 Go 与 RUST 在性能上确实不错,基本上与 C 是接近的,应用层到系统接口之间的层级比较薄</li>
<li>不同的操作系统表现不一样,但通常情况下Go都是在Linux系统下运行,所以应该以Linux系统的测试为准</li>
<li>Go、RUST相较C/C++而言,开发效率高很多,如果性能差不多的情况下,采用Go或RUST做服务器开发更有优势</li>
</ul>
<p><strong>但这次只是一个简单的测试,只能说在发送UDP时,Go、RUST与C性能差别不大,但并不代表在整体性能上Go和RUST已经赶上C/C++性能了。如果想更好的了解Go、RUST与C/C++的差异,应该做更详尽的测试验证。</strong></p>
<h2 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h2><p>代码地址:<a href="https://github.com/andancedu/go_rust_c.git">https://github.com/andancedu/go_rust_c.git</a></p>
<h2 id="我的课程"><a href="#我的课程" class="headerlink" title="我的课程"></a>我的课程</h2><p>-<a href="https://coding.imooc.com/class/415.html">音视频系统入门</a></p>
<p>-<a href="https://coding.imooc.com/class/279.html">ffmpeg精讲</a></p>
<p>-<a href="https://coding.imooc.com/class/329.html">WebRTC入门与实战</a></p>
<p>-<a href="https://coding.imooc.com/class/387.html">WebRTC高并发流媒体服务器</a></p>
<p>-<a href="https://time.geekbang.org/column/article/111337">从0开始构造直播系统</a></p>
<p>-<a href="https://coding.imooc.com/class/496.html">OpenCV入门与实战</a></p>
]]></content>
<categories>
<category>服务器</category>
<category>语言</category>
</categories>
<tags>
<tag>语言</tag>
<tag>GO</tag>
</tags>
</entry>
<entry>
<title>SDL事件处理</title>
<url>/a0ec02a7/</url>
<content><![CDATA[<p>前面我为大家介绍了 SDL 的三个主题:</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
</ul>
<p>今天我为大家介绍一下SDL的事件处理。这里所指的事件处理就是我们通常所说的,键盘事件,鼠标事件,窗口事件等。</p>
<p>SDL对这些事件都做了封装,提供了统一的API,下面我们就来详细的看一下。</p>
<span id="more"></span>
<h2 id="SDL中的事件处理"><a href="#SDL中的事件处理" class="headerlink" title="SDL中的事件处理"></a>SDL中的事件处理</h2><p>要想了解 SDL 的事件处理,我们必须要知道的一个原理是,SDL将所有事件都存放在一个队列中。所有对事件的操作,其实就是对队列的操作。了解了这个原理后,我们再来说SDL提供的 API 就很容易理解了。</p>
<ul>
<li>SDL_PollEvent: 将队列头中的事件抛出来。</li>
<li>SDL_WaitEvent: 当队列中有事件时,抛出事件。否则处于阻塞状态,释放 CPU。</li>
<li>SDL_WaitEventTimeout: 与SDL_WaitEvent的区别时,当到达超时时间后,退出阻塞状态。</li>
<li>SDL_PeekEvent: 从队列中取出事件,但该事件不从队列中删除。</li>
<li>SDL_PushEvent: 向队列中插入事件。</li>
</ul>
<p>SDL只提供了这样几个简单的API,下面们来介绍几个常见的事件:</p>
<ul>
<li>SDL_WindowEvent : Window窗口相关的事件。</li>
<li>SDL_KeyboardEvent : 键盘相关的事件。</li>
<li>SDL_MouseMotionEvent : 鼠标移动相关的事件。</li>
<li>SDL_QuitEvent : 退出事件。</li>
<li>SDL_UserEvent : 用户自定义事件。</li>
</ul>
<p>关于事件更加详的信息可以到 <a href="https://wiki.libsdl.org/SDL_Event">SDL Wiki</a> 上进行查询。现在我们来看一个使用的例子吧。</p>
<h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><p>在我们之前文章的例子中,大家已经发现一个问题,那就是窗口只显示了 3 秒钟,之后就自动消失了。</p>
<p>有的同学可以会通过修改代码最后面的 SDL_Delay 函数,增加它的等待时间让窗口多活一段时间。</p>
<p>但这样的体验实在是太糟糕了。有没有一种好的办法可以解决这个问题呢?能不能窗口一直显示,直到检测到用户按了<code>ctrl+c</code> 或 使用鼠标点击关闭按钮后才关闭呢?</p>
<p>当然是可以的。我们只需要在之前的程序的末尾增加下面这段代码即可。它会一直检测用户是否按下了退出按钮。如果检测到了,则直接退出,否则保持显示状态。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">while(!quit){</span><br><span class="line"> SDL_Event event;</span><br><span class="line"> while(SDL_PollEvent(&event)){</span><br><span class="line"> switch(event.type){</span><br><span class="line"> case SDL_QUIT:</span><br><span class="line"> quit = 1;</span><br><span class="line"> break;</span><br><span class="line"> default:</span><br><span class="line"> SDL_Log(".");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="SDL-PollEvent-与-SDL-WaitEvent"><a href="#SDL-PollEvent-与-SDL-WaitEvent" class="headerlink" title="SDL_PollEvent 与 SDL_WaitEvent"></a>SDL_PollEvent 与 SDL_WaitEvent</h2><p>增加了上面的代码,我们的实验程序似乎也显的很正规了。但有一个问题不知你发现没有<br>?当我们打开任务管理器时,发现我们的程序居然占了 100% 的 CPU。My GOD!这个的结果是决对不能接受的。</p>
<p>是什么原因造成的呢?我们来仔细看一下我们增加的代码吧。它由两层 while 循环组成,最里面的while循环的意思是,当队列中一直能取出事件,那就让他一直做下去,直到事件队列为空。外面的while循环的意思是,当队列为空的时候,重新执行内部的while循环。</p>
<p>也就是说,这段代码一直在工作,从不休息。所以导致cpu占到了100%。即然找到了问题的原因,我们就好处理了,只要在外层循环的最后 delay一下,让CPU休息一下就好了。</p>
<p>当然,SDL还为我们提供了 SDL_WaitEvent方法,使用这个API,你的CPU就不会跑到 100%了,因为当它发现队列为空时,它会阻塞在那里,并将CPU释放掉。</p>
<p>即然有 SDL_WaitEvent了,为什么还要有SDL_PollEvent呢?这主要是由于使用的场景不同。对于游戏来说,它要求事件的实时处理; 而对于一些其它实时性不高的case来说,则可以使用 SDL_WaitEvent了。</p>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>到此,本文的内容就介绍完了。在本文中主要介绍了SDL是如何处理事件的,SDL为我们提供了非常简单的API,这大大减少了我们的开发成本。</p>
<p>另外,我在文章的最后,介绍了SDL_PollEvent 与 SDL_WaitEvent两个 API的区别。这也是使用 SDL 事件处理中最容易引起困惑的地方。</p>
<p>希望本文能对您有所帮助,谢谢!</p>
<h2 id="推荐阅读:"><a href="#推荐阅读:" class="headerlink" title="推荐阅读:"></a>推荐阅读:</h2><ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
]]></content>
<categories>
<category>视频渲染</category>
</categories>
<tags>
<tag>SDL</tag>
</tags>
</entry>
<entry>
<title>SDL入门</title>
<url>/56ef4bcb/</url>
<content><![CDATA[<p>推荐阅读:</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
<p>SDL是 “Simple DirectMedia Layer”的缩写,它是一个开源的项目。其主要用于游戏开发中的多媒体处理,如视频渲染,音频播放,鼠标/键盘控制等操作。</p>
<p>并且它是一个跨平台的多媒体库。也就是说它对外接供了一套统一的接口,但在内部,它会根据不同平台调用不同的底层 API库。如在 Linux 系统下,它会使用 opengl 做渲染,而在 Window 下它会调用 D3D API进行渲染。</p>
<p>我之所以要介绍它,主要是因为我要在开发的多媒体播放器中使用它。</p>
<span id="more"></span>
<h2 id="SDL的编译与安装"><a href="#SDL的编译与安装" class="headerlink" title="SDL的编译与安装"></a>SDL的编译与安装</h2><p>目前 SDL 分为 SDL1 和 SDL2 两个主要版本。这两上版本差异非常大,无法相兼容。不过SDL1已经基本过时,主流产品都在使用的 SDL2,所以我们这里也使用SDL2作为例子进行讲解。</p>
<ul>
<li><p><a href="https://www.libsdl.org/download-2.0.php">下载SDL源码</a>(<strong>可能需要翻墙才行</strong>)</p>
</li>
<li><p>编译与安装</p>
<ul>
<li><p>生成SDL的Makefile</p>
<pre><code><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">configure --prefix=/usr/local</span><br></pre></td></tr></table></figure>
</code></pre>
</li>
<li><p>编译并安装</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">sudo make && make install</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h2 id="使用-SDL2-的其本流程"><a href="#使用-SDL2-的其本流程" class="headerlink" title="使用 SDL2 的其本流程"></a>使用 SDL2 的其本流程</h2><p>当我们通过源码编译并安装好 SDL2后,在我们的程序中使用 SDL2 就非常简单了,只要按照下面的步骤就可以绘制出一个窗口来。</p>
<ul>
<li><p>添加SDL头文件</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <SDL.h></span><br></pre></td></tr></table></figure></li>
<li><p>初始化SDL</p>
</li>
<li><p>创建窗口</p>
</li>
<li><p>销毁窗口</p>
</li>
<li><p>退出SDL</p>
</li>
</ul>
<p>当然,上面的步骤只是一个最基本的使用 SDL 的步骤,如果想了解更多的 SDL 的内容,静请期待我后面的文章。</p>
<h2 id="API详细介绍"><a href="#API详细介绍" class="headerlink" title="API详细介绍"></a>API详细介绍</h2><p>下面我们详细介绍一下上面用到的几个SDL API。</p>
<ul>
<li><p>初始化 SDL</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_Init(Uint32 flags)</span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th>flags</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>SDL_INIT_TIMER</td>
<td>timer subsystem</td>
</tr>
<tr>
<td>SDL_INIT_AUDIO</td>
<td>audio subsystem</td>
</tr>
<tr>
<td>SDL_INIT_VIDEO</td>
<td>video subsystem; automatically initializes the events subsystem</td>
</tr>
<tr>
<td>SDL_INIT_EVENTS</td>
<td>events subsystem</td>
</tr>
<tr>
<td>SDL_INIT_EVERYTHING</td>
<td>all of the above subsystems</td>
</tr>
</tbody></table>
<p>返回值:0, 成功。非0, 失败。</p>
</li>
<li><p>退出 SDL</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_Quit(void)</span><br></pre></td></tr></table></figure>
</li>
<li><p>打印日志</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_Log(const char* fmt, ...)</span><br></pre></td></tr></table></figure>
<p>它与 C 语言中的 printf 格式相同。</p>
</li>
<li><p>创建窗口</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">SDL_Window* SDL_CreateWindow(const char* title,</span><br><span class="line"> int x,</span><br><span class="line"> int y,</span><br><span class="line"> int w,</span><br><span class="line"> int h,</span><br><span class="line"> Uint32 flags)</span><br></pre></td></tr></table></figure>
<ul>
<li><p>title:窗口标题</p>
</li>
<li><p>x,y,w,h:窗口坐标</p>
</li>
<li><p>flag</p>
<table>
<thead>
<tr>
<th>flags</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>SDL_WINDOW_FULLSCREEN</td>
<td>fullscreen window</td>
</tr>
<tr>
<td>SDL_WINDOW_FULLSCREEN_DESKTOP</td>
<td>fullscreen window at the current desktop resolution</td>
</tr>
<tr>
<td>SDL_WINDOW_OPENGL</td>
<td>window usable with OpenGL context</td>
</tr>
<tr>
<td>SDL_WINDOW_HIDDEN</td>
<td>window is not visible</td>
</tr>
<tr>
<td>SDL_WINDOW_BORDERLESS</td>
<td>no window decoration</td>
</tr>
<tr>
<td>SDL_WINDOW_RESIZABLE</td>
<td>window can be resized</td>
</tr>
<tr>
<td>SDL_WINDOW_MINIMIZED</td>
<td>window is minimized</td>
</tr>
<tr>
<td>SDL_WINDOW_MAXIMIZED</td>
<td>window is maximized</td>
</tr>
<tr>
<td>SDL_WINDOW_SHOWN</td>
<td>show window</td>
</tr>
</tbody></table>
</li>
</ul>
</li>
<li><p>销毁窗口</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_DestroyWindow(SDL_Window* window)</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><p>下面是一个完整的使用SDL创建窗口的例子,你可以在 linux/mac环境下执行它。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "SDL.h"</span><br><span class="line">#include <stdio.h></span><br><span class="line"></span><br><span class="line">int main(int argc, char* argv[]) {</span><br><span class="line"></span><br><span class="line"> int flag = 1;</span><br><span class="line"></span><br><span class="line"> SDL_Window *window; // Declare a pointer</span><br><span class="line"></span><br><span class="line"> SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2</span><br><span class="line"></span><br><span class="line"> // Create an application window with the following settings:</span><br><span class="line"> window = SDL_CreateWindow(</span><br><span class="line"> "An SDL2 window", // window title</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED, // initial x position</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED, // initial y position</span><br><span class="line"> 640, // width, in pixels</span><br><span class="line"> 480, // height, in pixels</span><br><span class="line"> SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS// flags - see below</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> // Check that the window was successfully created</span><br><span class="line"> if (window == NULL) {</span><br><span class="line"> // In the case that the window could not be made...</span><br><span class="line"> printf("Could not create window: %s\n", SDL_GetError());</span><br><span class="line"> return 1;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // The window is open: could enter program loop here (see SDL_PollEvent())</span><br><span class="line"></span><br><span class="line"> SDL_Delay(3000); // Pause execution for 3000 milliseconds, for example</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> // Close and destroy the window</span><br><span class="line"> SDL_DestroyWindow(window);</span><br><span class="line"></span><br><span class="line"> // Clean up</span><br><span class="line"> SDL_Quit();</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>你可以使用下面的命令在linux/mac上编译上面的程序。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">gcc/clang -g -o sdl2_base 文件名.c `pkg-config --cflags --libs sdl2`</span><br></pre></td></tr></table></figure>
<p>编译出的程序名为 sdl2_base,执行下面的命令就可能看到运行的结果:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">./sdl2_base</span><br></pre></td></tr></table></figure>
<p><strong>需要注意的是,虽然上面的程序可以正常编译执行,但你会发现该程序创建的窗口并不能显示出来。我会在第二篇文章中介绍如何让窗口正常的显示出来</strong></p>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>通过本文的介绍大家是不是觉得使用 SDL 非常简单易用呢?当然也许有人不太喜欢 SDL,认为直接使用 opengl 更高效。</p>
<p>但你要知道,SDL不只是对图像渲染做了封装,它还对其它媒体API做了封装,如对音频处理的封装等。这些封装大大减少了我们的开发工作量。</p>
<p>从另一方面讲,SDL 是一款非常优秀有多媒体库,除了使用它,其实我们还可以通过对其源码的分析,从中学到很多使用底层API的技巧。尤其是想学习播放器开发的同学,更是应该学好 SDL,因为著名的 ffplay 就是用的 SDL 做视频和音频的最终渲染与播放的。</p>
<p>最后,希望本文能帮你进行到 SDL 的世界。</p>
<h2 id="推荐阅读:"><a href="#推荐阅读:" class="headerlink" title="推荐阅读:"></a>推荐阅读:</h2><ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
]]></content>
<categories>
<category>视频渲染</category>
</categories>
<tags>
<tag>SDL</tag>
<tag>视频渲染</tag>
</tags>
</entry>
<entry>
<title>SDL彻底理解纹理</title>
<url>/67189745/</url>
<content><![CDATA[<p>这是SDL系列文章的第五篇,本文将彻底让你理解什么是纹理。并带你深入探讨SDL的几个重要概念SDL_Window、SDL_Render、SDL_Surface 与 SDL_Texture。在文章的最后向你展示SDL如何通过SDL_Texture进行渲染。</p>
<p>对于前面系列文章感兴趣的同学可以通过下面的链接查看:</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
<span id="more"></span>
<h2 id="SDL-Surface-vs-SDL-Texture"><a href="#SDL-Surface-vs-SDL-Texture" class="headerlink" title="SDL_Surface vs SDL_Texture"></a>SDL_Surface vs SDL_Texture</h2><p>在SDL系列文章的第二篇里,我详细的介绍了SDL 渲染的工作原理。即在SDL_Render对象中有一个视频缓冲区,该缓冲区我们称之为SDL_Surface,它是按照像素存放图像的。我们一般把真彩色的像素称为RGB24数据。也就是说,每一个像素由24位组成,每8位代表一种颜色,像素的最终颜色是由RGB三种颜色混合而成的。</p>
<p>SDL_Texture 与SDL_Surface相似,也是一种缓冲区。只不过它存放的不是真正的像素数据,而是存放的图像的描述信息。当渲染纹理时,SDL以这些描述信息为数据,底层通过OpenGL、D3D 或 Metal操作GPU,最终绘制出与SDL_Surface一样的图形,且效率更高(因为它是GPU硬件计算的)。</p>
<p>看了以上的介绍,是不是对纹理有了一个清楚的认识了?</p>
<p>介绍完 SDL_Surface 和 SDL_Texture后,我们再看下SDL_Window 与 SDL_Render。</p>
<h2 id="SDL-Window-与-SDL-Render"><a href="#SDL-Window-与-SDL-Render" class="headerlink" title="SDL_Window 与 SDL_Render"></a>SDL_Window 与 SDL_Render</h2><p>SDL_Window代表的是窗口的逻辑概念,它是存放在主内存中的一个对象。所以当我们调用SDL API 创建窗口后,它并不会被显示出来。</p>
<p>SDL_Render 是渲染器,它也是主存中的一个对象。对Render操作时实际上分为两个阶段:</p>
<p>一、渲染阶段。在该阶段,用户可以画各种图形渲染到SDL_Surface或SDL_Texture 中;</p>
<p>二、显示阶段。参SDL_Texture为数据,通过OpenGL操作GPU,最终将 SDL_Surfce 或SDL_Texture中的数据输出到显示器上。</p>
<p>通过上面的介绍,我们就将 SDL_Window、SDL_Render、SDL_Surface与 SDL_Texture之间的关系梳理清楚了,下面我们来看一下如何使用 SDL_Texture。</p>
<h2 id="使用SDL-Texture"><a href="#使用SDL-Texture" class="headerlink" title="使用SDL_Texture"></a>使用SDL_Texture</h2><p>SDL提供了非常好用的操作SDL_Texture的方法,下面我们来重点介绍一下使用SDL_Texute的基本步骤。</p>
<ul>
<li><p>创建一个 SDL_Texture。</p>
</li>
<li><p>渲染 Texture</p>
</li>
<li><p>Destory Texture</p>
</li>
</ul>
<h2 id="API详细介绍"><a href="#API详细介绍" class="headerlink" title="API详细介绍"></a>API详细介绍</h2><ul>
<li><p>创建 SDL_Texture</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer,</span><br><span class="line"> Uint32 format,</span><br><span class="line"> int access,</span><br><span class="line"> int w,</span><br><span class="line"> int h)</span><br></pre></td></tr></table></figure>
<ul>
<li>format: 指明像素格式,可以是YUV,也可以是RGB</li>
<li>access: 指明Texture的类型。可以是 Stream(视频),也可以是Target一般的类型。</li>
</ul>
</li>
<li><p>渲染</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderCopy(SDL_Renderer* renderer,</span><br><span class="line"> SDL_Texture* texture,</span><br><span class="line"> const SDL_Rect* srcrect,</span><br><span class="line"> const SDL_Rect* dstrect)</span><br></pre></td></tr></table></figure>
<ul>
<li>srcrect: 指定 Texture 中要渲染的一部分。如果将 Texture全部输出,可以设置它为 NULL。</li>
<li>dstrect: 指定输出的空间大小。</li>
</ul>
</li>
<li><p>销毁Texture</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_DestroyTexture(SDL_Texture* texture)</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><p>下面这个例子非常简单,我这里就不做特别的说明了。对这个程序看不懂的同学可以看我之前的几篇 SDL 的相关文章。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "SDL.h"</span><br><span class="line"></span><br><span class="line">/* Moving Rectangle */</span><br><span class="line">int main(int argc, char *argv[])</span><br><span class="line">{</span><br><span class="line"> SDL_Window *window;</span><br><span class="line"> SDL_Renderer *renderer;</span><br><span class="line"> SDL_Texture *texture;</span><br><span class="line"> SDL_Event event;</span><br><span class="line"> SDL_Rect r;</span><br><span class="line"></span><br><span class="line"> if (SDL_Init(SDL_INIT_VIDEO) < 0) {</span><br><span class="line"> SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());</span><br><span class="line"> return 3;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> window = SDL_CreateWindow("SDL_CreateTexture",</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED,</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED,</span><br><span class="line"> 1024, 768,</span><br><span class="line"> SDL_WINDOW_RESIZABLE);</span><br><span class="line"></span><br><span class="line"> r.w = 100;</span><br><span class="line"> r.h = 50;</span><br><span class="line"></span><br><span class="line"> renderer = SDL_CreateRenderer(window, -1, 0);</span><br><span class="line"></span><br><span class="line"> texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 1024, 768);</span><br><span class="line"></span><br><span class="line"> while (1) {</span><br><span class="line"> SDL_PollEvent(&event);</span><br><span class="line"> if(event.type == SDL_QUIT)</span><br><span class="line"> break;</span><br><span class="line"> r.x=rand()%500;</span><br><span class="line"> r.y=rand()%500;</span><br><span class="line"></span><br><span class="line"> SDL_SetRenderTarget(renderer, texture);</span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);</span><br><span class="line"> SDL_RenderClear(renderer);</span><br><span class="line"> SDL_RenderDrawRect(renderer,&r);</span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0x00);</span><br><span class="line"> SDL_RenderFillRect(renderer, &r);</span><br><span class="line"> SDL_SetRenderTarget(renderer, NULL);</span><br><span class="line"> SDL_RenderCopy(renderer, texture, NULL, NULL);</span><br><span class="line"> SDL_RenderPresent(renderer);</span><br><span class="line"> }</span><br><span class="line"> SDL_DestroyRenderer(renderer);</span><br><span class="line"> SDL_Quit();</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>本文重点介绍了 SDL_Window、SDL_Render、SDL_Surface以及SDL_Texture之间的关系。搞清楚它们之前的关系对于理解 SDL 渲染起着至关重要的作用。</p>
<p>大家一定要仔细的理解文章中所讲的内容,在我后序的文章中,尤其是后面介绍 播放器 相关内容时,都要用到现在所讲的这些内容。</p>
<p>希望本文能对你有所帮助,谢谢!</p>
<h2 id="隆重推荐"><a href="#隆重推荐" class="headerlink" title="隆重推荐"></a>隆重推荐</h2><ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
<li><a href="https://coding.imooc.com/class/279.html">ffmpeg精讲</a></li>
</ul>
]]></content>
<categories>
<category>视频渲染</category>
</categories>
<tags>
<tag>SDL</tag>
</tags>
</entry>
<entry>
<title>SDL绘制基本图型</title>
<url>/24ee78a8/</url>
<content><![CDATA[<p>之前的SDL的两篇文章我向大家介绍了如何编译使用 SDL,以及如何才能让窗口显示出来。想了解相关内容的同学可以点击下面的链接查看相关内容。</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
<p>本文将向大家介绍一下,如何通过 SDL 绘制一些基本图形,如 点、线、矩形。了解了这些基本图形后,你就可以按照搭积木的方式,构造出其它更复杂的图形了。</p>
<span id="more"></span>
<h2 id="有哪些基本图形可以绘制"><a href="#有哪些基本图形可以绘制" class="headerlink" title="有哪些基本图形可以绘制"></a>有哪些基本图形可以绘制</h2><p>SDL中绘制基本图形的 API并不多,主要是 点、线、矩形。其它图形都可以通过 点、线、矩形组合出来。</p>
<ul>
<li>设置颜色:在绘制图形前,要设置一下画笔的色彩。这里需要注意的是,如果画笔与背景色相同了,那在窗口中是显示不出来图形的。</li>
<li>画点。</li>
<li>画线。</li>
<li>画矩形。</li>
<li>填充矩形。</li>
</ul>
<p>下面来详细介绍一下这几个API。</p>
<h2 id="API详细介绍"><a href="#API详细介绍" class="headerlink" title="API详细介绍"></a>API详细介绍</h2><ul>
<li><p>设置颜色</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_SetRenderDrawColor(SDL_Renderer* renderer,</span><br><span class="line"> Uint8 r,</span><br><span class="line"> Uint8 g,</span><br><span class="line"> Uint8 b,</span><br><span class="line"> Uint8 a)</span><br></pre></td></tr></table></figure>
<p>该函数中的参数 a 指明了颜色的透明度。</p>
<p><strong>但该值我设置了一下没有起作用,应该需要和BlendMode一起才能起作用。这块有谁清楚可以指定一下</strong></p>
</li>
<li><p>画点</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderDrawPoint(SDL_Renderer* renderer,</span><br><span class="line"> int x, </span><br><span class="line"> int y)</span><br></pre></td></tr></table></figure>
</li>
<li><p>画多个点</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderDrawPoints(SDL_Renderer* renderer,</span><br><span class="line"> const SDL_Point* points,</span><br><span class="line"> int count)</span><br></pre></td></tr></table></figure>
<ul>
<li>points: 点数组。</li>
<li>count: 点的个数。</li>
</ul>
</li>
<li><p>画线</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderDrawLine(SDL_Renderer* renderer,</span><br><span class="line"> int x1,</span><br><span class="line"> int y1,</span><br><span class="line"> int x2,</span><br><span class="line"> int y2)</span><br></pre></td></tr></table></figure>
</li>
<li><p>画多条线</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderDrawLines(SDL_Renderer* renderer,</span><br><span class="line"> const SDL_Point* points,</span><br><span class="line"> int count)</span><br></pre></td></tr></table></figure>
<p>该函数会将使用两个相邻的点之间进行连线。最终画出你想画的图形。如画三角形,多边形或圆形。</p>
</li>
<li><p>绘制矩形</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderDrawRect(SDL_Renderer* renderer,</span><br><span class="line"> const SDL_Rect* rect)</span><br></pre></td></tr></table></figure>
<p>rect: 是要绘制的一块区域。它包括,x,y,w,h这个元素。</p>
</li>
<li><p>填充矩形</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderFillRect(SDL_Renderer* renderer,</span><br><span class="line"> const SDL_Rect* rect)</span><br></pre></td></tr></table></figure>
<p>使用指定的色彩填充一块矩形。</p>
</li>
<li><p>填充多块矩形</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderDrawRects(SDL_Renderer* renderer,</span><br><span class="line"> const SDL_Rect* rects,</span><br><span class="line"> int count)</span><br></pre></td></tr></table></figure>
<ul>
<li>rects: 指定的矩形数组。</li>
<li>count: 指定矩形个数。</li>
</ul>
</li>
</ul>
<h2 id="我们来看看代码"><a href="#我们来看看代码" class="headerlink" title="我们来看看代码"></a>我们来看看代码</h2><p>下面的代码非常之简单,我们在上一篇文章代码的基础上增加了几个画线、画矩形的API就可以了。</p>
<p>这里唯一值得注意的地方是下面这个函数。 </p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);</span><br></pre></td></tr></table></figure>
<p>该函数是设置画笔颜色,也就是说我们想画出什么颜色的图形,只要用这个函数设置一下,再使用画点、画线的API就可以画出对应颜色的图形了。</p>
<p>原码如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "SDL.h"</span><br><span class="line">#include <stdio.h></span><br><span class="line"></span><br><span class="line">#define POINTS_COUNT 4</span><br><span class="line"></span><br><span class="line">static SDL_Point points[POINTS_COUNT] = {</span><br><span class="line"> {320, 200},</span><br><span class="line"> {300, 240},</span><br><span class="line"> {340, 240},</span><br><span class="line"> {320, 200}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">static SDL_Rect bigrect = {0,0,540, 380};</span><br><span class="line"></span><br><span class="line">int main(int argc, char* argv[]) {</span><br><span class="line"></span><br><span class="line"> int flag = 1;</span><br><span class="line"></span><br><span class="line"> SDL_Window *window; // Declare a pointer</span><br><span class="line"> SDL_Renderer *renderer;</span><br><span class="line"></span><br><span class="line"> SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2</span><br><span class="line"></span><br><span class="line"> // Create an application window with the following settings:</span><br><span class="line"> window = SDL_CreateWindow(</span><br><span class="line"> "An SDL2 window", // window title</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED, // initial x position</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED, // initial y position</span><br><span class="line"> 640, // width, in pixels</span><br><span class="line"> 480, // height, in pixels</span><br><span class="line"> SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS// flags - see below</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> // Check that the window was successfully created</span><br><span class="line"> if (window == NULL) {</span><br><span class="line"> // In the case that the window could not be made...</span><br><span class="line"> printf("Could not create window: %s\n", SDL_GetError());</span><br><span class="line"> return 1;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> /* We must call SDL_CreateRenderer in order for draw calls to affect this window. */</span><br><span class="line"> renderer = SDL_CreateRenderer(window, -1, 0);</span><br><span class="line"></span><br><span class="line"> /* Select the color for drawing. It is set to red here. */</span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);</span><br><span class="line"></span><br><span class="line"> /* Clear the entire screen to our selected color. */</span><br><span class="line"> SDL_RenderClear(renderer);</span><br><span class="line"></span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);</span><br><span class="line"> //SDL_RenderDrawLine(renderer, 100, 20, 500, 400);</span><br><span class="line"> SDL_RenderDrawLines(renderer, points, POINTS_COUNT);</span><br><span class="line"></span><br><span class="line"> SDL_Rect rect = {200, 300, 100, 100};</span><br><span class="line"> SDL_RenderDrawRect(renderer, &rect);</span><br><span class="line"></span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);</span><br><span class="line"> SDL_RenderFillRect(renderer, &rect);</span><br><span class="line"></span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);</span><br><span class="line"> SDL_RenderFillRect(renderer, &bigrect);</span><br><span class="line"></span><br><span class="line"> /* Up until now everything was drawn behind the scenes.</span><br><span class="line"> This will show the new, red contents of the window. */</span><br><span class="line"> SDL_RenderPresent(renderer);</span><br><span class="line"></span><br><span class="line"> // The window is open: could enter program loop here (see SDL_PollEvent())</span><br><span class="line"></span><br><span class="line"> SDL_Delay(3000); // Pause execution for 3000 milliseconds, for example</span><br><span class="line"></span><br><span class="line"> //destory renderer</span><br><span class="line"> if (renderer) {</span><br><span class="line"> SDL_DestroyRenderer(renderer);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // Close and destroy the window</span><br><span class="line"> SDL_DestroyWindow(window);</span><br><span class="line"></span><br><span class="line"> // Clean up</span><br><span class="line"> SDL_Quit();</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>本文重点介绍了 SDL 绘制经常使用的几个基本图形API,通过这些API你可以构造出更加复杂的图形。</p>
<p>希望本文能对你所有帮助,谢谢!</p>
<h2 id="推荐阅读:"><a href="#推荐阅读:" class="headerlink" title="推荐阅读:"></a>推荐阅读:</h2><ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
]]></content>
<categories>
<category>视频渲染</category>
</categories>
<tags>
<tag>SDL</tag>
</tags>
</entry>
<entry>
<title>SDL窗口渲染</title>
<url>/287ad9ab/</url>
<content><![CDATA[<p>推荐阅读:</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
<p>上一篇文章中我们对SDL作了简单的介绍,重点介绍了如何编译SDL以及如何使用它。在文章的最后我们留下了一个疑问,即虽然我们创建了窗口,但窗口却并没有真正显示出来。</p>
<p>今天我们就来看一看,如何才能让创建的窗口真正的显示出来。</p>
<span id="more"></span>
<h2 id="渲染的基本流程"><a href="#渲染的基本流程" class="headerlink" title="渲染的基本流程"></a>渲染的基本流程</h2><p>为什么我们上一课中创建了窗口,但它却并没有显示出来呢?其原因是,我们创建的窗口只是逻辑上的窗口,要想让窗口显示出来,我们需要对窗口进行效果渲染,也就是要通过绘制像素的方法,将窗口中的像素全部点亮。</p>
<p>那么如何对窗口进行渲染呢?SDL为我们提供了方便是的API。不过在使用SDL对窗口进行渲染之前,我们要先了解渲染的基本原理。</p>
<p>其基本原理是,首先创建一个window窗口,它是我们要渲染的目标。然后,要有一个渲染上下文,该上下文中一方面存放着要渲染的目标,也就是windows窗口;另一方面是存放着一个缓冲区,该缓冲区用于存放渲染的内容。</p>
<p>渲染的内容可以是点、线、各种图形以及图片,视频的各种组合。这些组合后的内容首先被存放到缓冲区中,最终SDL将缓冲区中的内容渲染到窗口中。</p>
<p>所以渲染的基本流程如下:</p>
<ul>
<li>创建窗口</li>
<li>创建渲染器</li>
<li>清空缓冲区</li>
<li>绘制要显示的内容</li>
<li>最终将缓冲区内容渲染到window窗口上。</li>
</ul>
<h2 id="常用API"><a href="#常用API" class="headerlink" title="常用API"></a>常用API</h2><ul>
<li><p>创建渲染上下文</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">SDL_Renderer* SDL_CreateRenderer(SDL_Window* window,</span><br><span class="line"> int index,</span><br><span class="line"> Uint32 flags)</span><br></pre></td></tr></table></figure>
<p>window: 指明在哪个窗口里进行渲染<br>index: 指定渲染驱动的索引号。一般指定为 -1.<br>flags:</p>
<table>
<thead>
<tr>
<th>flags</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>SDL_RENDERER_SOFTWARE</td>
<td>the renderer is a software fallback</td>
</tr>
<tr>
<td>SDL_RENDERER_ACCELERATED</td>
<td>the renderer uses hardware acceleration</td>
</tr>
<tr>
<td>SDL_RENDERER_PRESENTVSYNC</td>
<td>present is synchronized with the refresh rate</td>
</tr>
<tr>
<td>SDL_RENDERER_TARGETTEXTURE</td>
<td>the renderer supports rendering to texture</td>
</tr>
</tbody></table>
</li>
<li><p>消毁渲染上下文</p>
<p>释放渲染上下文相关的资源。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_DestroyRenderer(SDL_Renderer* renderer)</span><br></pre></td></tr></table></figure></li>
<li><p>清空渲染目标</p>
<p>该函数的作用是用指定的颜色清空缓冲区。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_RenderClear(SDL_Renderer* renderer)</span><br></pre></td></tr></table></figure>
<p>renderer: 上面创建的渲染器上下文。</p>
</li>
<li><p>展示要渲染的内容</p>
<p>将缓冲区中的内容输出到目标上,也就是 windows 窗口上。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_RenderPresent(SDL_Renderer* renderer)</span><br></pre></td></tr></table></figure>
<p>renderer: 上面创建的渲染器上下文</p>
</li>
</ul>
<h2 id="完整例子"><a href="#完整例子" class="headerlink" title="完整例子"></a>完整例子</h2><p>我在第一课的代码上,添加了上面几个函数之后,大家可以看到一个全红色的窗口可以显示在我们的面前了。</p>
<p>当然我们还可以在上面画一些图形,比如使用 SDL_RenderDrawLines() 函数在窗口中画一条直线。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include "SDL.h"</span><br><span class="line">#include <stdio.h></span><br><span class="line"></span><br><span class="line">int main(int argc, char* argv[]) {</span><br><span class="line"></span><br><span class="line"> int flag = 1;</span><br><span class="line"></span><br><span class="line"> SDL_Window *window; // Declare a pointer</span><br><span class="line"> SDL_Renderer *renderer;</span><br><span class="line"></span><br><span class="line"> SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2</span><br><span class="line"></span><br><span class="line"> // Create an application window with the following settings:</span><br><span class="line"> window = SDL_CreateWindow(</span><br><span class="line"> "An SDL2 window", // window title</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED, // initial x position</span><br><span class="line"> SDL_WINDOWPOS_UNDEFINED, // initial y position</span><br><span class="line"> 640, // width, in pixels</span><br><span class="line"> 480, // height, in pixels</span><br><span class="line"> SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS// flags - see below</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> // Check that the window was successfully created</span><br><span class="line"> if (window == NULL) {</span><br><span class="line"> // In the case that the window could not be made...</span><br><span class="line"> printf("Could not create window: %s\n", SDL_GetError());</span><br><span class="line"> return 1;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /* We must call SDL_CreateRenderer in order for draw calls to affect this window. */</span><br><span class="line"> renderer = SDL_CreateRenderer(window, -1, 0);</span><br><span class="line"></span><br><span class="line"> /* Select the color for drawing. It is set to red here. */</span><br><span class="line"> SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);</span><br><span class="line"></span><br><span class="line"> /* Clear the entire screen to our selected color. */</span><br><span class="line"> SDL_RenderClear(renderer);</span><br><span class="line"></span><br><span class="line"> /* Up until now everything was drawn behind the scenes.</span><br><span class="line"> This will show the new, red contents of the window. */</span><br><span class="line"> SDL_RenderPresent(renderer);</span><br><span class="line"></span><br><span class="line"> // The window is open: could enter program loop here (see SDL_PollEvent())</span><br><span class="line"></span><br><span class="line"> SDL_Delay(3000); // Pause execution for 3000 milliseconds, for example</span><br><span class="line"></span><br><span class="line"> //destory renderer</span><br><span class="line"> if (renderer) {</span><br><span class="line"> SDL_DestroyRenderer(renderer);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> // Close and destroy the window</span><br><span class="line"> SDL_DestroyWindow(window);</span><br><span class="line"></span><br><span class="line"> // Clean up</span><br><span class="line"> SDL_Quit();</span><br><span class="line"> return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>本文我向大家介绍了如何将创建的窗口展示出来,并重点介绍了窗口渲染的基本原理以及使用的 SDL API。</p>
<p>后面的文章我将向大家重点介绍如何在窗口绘制一些常用图形。</p>
<p>谢谢!</p>
<h2 id="推荐阅读:"><a href="#推荐阅读:" class="headerlink" title="推荐阅读:"></a>推荐阅读:</h2><ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
]]></content>
<categories>
<category>视频渲染</category>
</categories>
<tags>
<tag>SDL</tag>
</tags>
</entry>
<entry>
<title>TypeScript入门</title>
<url>/48922786/</url>
<content><![CDATA[<p><img data-src="https://cdn.avdancedu.com/image/article/typescript/typescript.jpg" alt="TypeScript"><br>我们要学习一门新知识,首先要了解一些新知识的历史。<code>TypeScript</code> 是微软开发的,它的出现是为了解决 <code>JavaScript</code> 没有类型检查的弊端。因此,<code>TypeScript</code>并不是一门新语言,它的作用就是帮<code>JavaScript</code>检查数据类型是否正确,所以称它为<code>JavaScript</code>的一个<code>超集</code>是更贴切的。</p>
<p>有很多同学对 <code>TypeScript</code> 产生疑惑,他们会问 <code>TypeScript</code> 可以在浏览器上运行吗?</p>
<span id="more"></span>
<p>虽然<code>TypeScript</code>是<code>JavaScript</code>的一个<code>超集</code>,但由于它要进行类型检查,所以就与 JavaScript 的语法有一些不同了,因此 TypeScript 编写的程序是无法直接运行在浏览器上的。</p>
<h2 id="TypeScript-工作原理"><a href="#TypeScript-工作原理" class="headerlink" title="TypeScript 工作原理"></a>TypeScript 工作原理</h2><p>在讲解 <code>TypeScript</code> 工作原理之前,有一句话是你必须清楚且要印在脑子里的: <strong>在浏览器上,只能运行 JavaScript 脚本</strong>。</p>
<p>既然浏览器里只能运行<code>JavaScript</code>,那<code>TypeScript</code>具体又做了什么事儿呢?下面我就向你解释一下<code>TypeScript</code>都做了哪些事儿。</p>
<p>学习过编译原理的同学都清楚,<strong>类型检查</strong>就是<strong>语法分析</strong>,它属于编译器的范筹。TypeScript官网说: <strong>“TypeScrpt是JavaScript的超集,可以进行JavaScript类型检查。”</strong> 表明<code>TypeScript</code>为<code>JavaScript</code>增加了<strong>类型语法</strong>,并且它还有一个<strong>编译器</strong>,可以将用<code>TypeScript</code>语法编写的程序(.ts文件),<strong>翻译成</strong> <code>JavaScript</code> 脚本语言。</p>
<p>因此,<code>TypeScript</code>的使用步骤一定是这样的: 首先用 <code>TypeScript</code> 语法编写程序;然后<strong>编译</strong>,输出<code>JavaScript</code>脚本;最终在浏览器中引用生成的 JavaScript 脚本这样一个过程。</p>
<p>实际情况也确实如此,在TypeScript的官网上你可以找到,TypeScript 有一个编译工具称为 <code>tsc</code>,即 <code>t</code>(ype) <code>s</code>(cript) <code>c</code>(ompiler)的缩写 。</p>
<h2 id="JavaScript的最大问题"><a href="#JavaScript的最大问题" class="headerlink" title="JavaScript的最大问题"></a>JavaScript的最大问题</h2><p>使用过其它面向对象语言的同学们再使用JavaScript开发时,都会觉得特别<code>难受</code>,因为JavaScript无法进行类型检查。对于Javascript语法来说,你传给它什么类型的数据都可以,这样虽然看似很<code>很好</code>,但一旦你将数据传错了,你得到的结果就是错误的。当错误出现时,没有任何的提示,光凭我们肉眼去查找错误实在是太费力了,这就是JavaScript的最大问题。</p>
<p>我们来看一个最简单的JavaScript例子你应该就清楚了:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">function func(msg){</span><br><span class="line"> console.log("the message is :" + msg);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面这段代码是一段最简单的JS代码,它需要一个字符串参数。但由于JavaScript并没有对参数的类型做限制,所以你在调用这个函数时,可以给他传任意的值。如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">func(123);</span><br><span class="line">func('this is my book!');</span><br><span class="line">func(func);</span><br></pre></td></tr></table></figure>
<p>我的本意是只有第二种调用<code>func</code>的方式才是正确的,但在JavaScript中上面这几种调用<code>func</code>函数的方法都是正确的。在我们实际工作中,我们写了很长的代码后,传入了一个错误的值时JavaScript就无法为我们检测出那里出错了,这是最可怕的。</p>
<p>这也是为什么<code>TypeScript</code>逐渐受到大家喜欢的原因。</p>
<h2 id="一个最简单的-TypeScript-程序"><a href="#一个最简单的-TypeScript-程序" class="headerlink" title="一个最简单的 TypeScript 程序"></a>一个最简单的 TypeScript 程序</h2><p>下面我们就来写一个最简单的 TypeScript程序,了解一下整个使用过程。咱们还是以 Helloworld 为例:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">function hello(person: string) {</span><br><span class="line"> return 'Hello , ' + person;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">let user='xxx'</span><br><span class="line">console.log(hello(user));</span><br></pre></td></tr></table></figure>
<p>上面就是一个简单的 TypeScript脚本,有了这个脚本后我们需要先对其进行编译,编译执行下面的命令:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">tsc hello.ts</span><br></pre></td></tr></table></figure>
<p>编译出 JavaScript 脚本后,通过 HTML5 引用一下就好了,代码如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><head></span><br><span class="line"> <head></span><br><span class="line"> <title>user typescript</title></span><br><span class="line"> </head></span><br><span class="line"> <body></span><br><span class="line"> <script type="text/javascript" src="./hellots.js"></script></span><br><span class="line"> </body></span><br><span class="line"></head></span><br></pre></td></tr></table></figure>
<p>至此,一个 TypeScript的简单使用过程就完成了,还是蛮简单的对吧!</p>
<h2 id="定义一个类"><a href="#定义一个类" class="headerlink" title="定义一个类"></a>定义一个类</h2><p>使用TypeScript定义类非常简单,这里我们直接看代码就好了:</p>
<figure class="highlight typescript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"> id : <span class="built_in">number</span></span><br><span class="line"> <span class="attr">msg</span>: <span class="built_in">string</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//构造函数</span></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">id: <span class="built_in">number</span>, msg: <span class="built_in">string</span></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">id</span> = id</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">msg</span> = msg</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面我定义了一个<strong>MyClass</strong> 类,它包括id和msg两个成员,其中id是数值型,而msg是字符串型。</p>
<p>在这个类中有一个构造函数,即 <strong>constructor</strong>,我们需要在这个函数中对类中的成员变量进行初始化。</p>
<h2 id="TS-调用JS"><a href="#TS-调用JS" class="headerlink" title="TS 调用JS"></a>TS 调用JS</h2><p>我们在使用TS开发时最常见的一个问题是如何通过 TS 调用 JS。产生该问题的根本原因是一些著名的 JS 库(如VUE)的TS版本还没有开发出来,但我们的项目中又必须使用他们,因此就产生了我前面所说的TS 库要调用 JS 库的情况。</p>
<p>我们前面也讲了 TS 本来就是 JS 的一个<code>超集</code>,因此它是有办法调用JS库的。从原理上我们可以证明这一点,因为TS程序最终要经过编译生成JS脚本程序后才能被浏览器执行,因此只要在浏览器上引入它需要的 JS 库,经编译后的 TS 程序就可以与其它的JS库相互调用。</p>
<p>按照上面的描述好像TS 调JS是很简单的事儿,那问题在哪儿呢?其实问题在于如果我们自己写的 TS 程序调用了其它第三方 JS 库,那么正常情况下 <code>tsc</code> 编译我们的 <code>ts</code> 程序时就会报错。所以实际的问题应该是,我们编写的TS程序在调用第三方JS库时,如何可以顺利编译通过呢?</p>
<p>我们在网上可以找到以下几种方案,第一种是将引用到的 JS 库直接翻译成 TS 库。很多同学看到这句话会<code>嗤之以鼻</code>,这明显就是行不通的方案。不过这种方案虽然难度很大,但总归还是一种方案不是;第二种是让<code>tsc</code>编译器不进行类型检查,那也就失去了 <code>TS</code> 的意义,所以这种方法也不是好办法;第三种是为 JS 库增加声名函数,这种方式既可以让<code>tsc</code>在编译时顺利通过,又同时可以检查我们写的代码是否有问题。</p>
<p>所以综上所述,第三种方案是最优的方案。</p>
<p>接下来我就来重点讲解一下如何通过第三种方案实现<code>TS</code>调用第三方<code>JS库</code>。首先我们先来创建一个JS库<code>test.js</code>,代码如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">function showmsg(msg){</span><br><span class="line"> console.log("the msg is :" + msg);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面的代码非常简单,只实现了一个函数。该函数有一个参数<code>msg</code>,当该函数被调用时,输出传入的参数值。</p>
<p>接下来我们来看一下在<code>TS</code>中如何调用JS中的方法。为了使 TS 可以调用JS的方法,我们需要写一个与<code>JS</code>文件名对应的<code>.d.ts</code>文件。对于我们上面的<code>test.js</code>则需要有一个<code>test.d.ts</code>与之对应。</p>
<p>编写<code>.d.ts</code>文件也很简单,对于test.js来说,它的<code>.d.ts</code>文件内容如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">declare function showmsg( msg: string) : void;</span><br></pre></td></tr></table></figure>
<p>只需要这样一句就可以了。实际上,它就是为test.js文件中的showmsg函数定个声名。然后我们在使用它的TS文件中引用这个<code>.d.ts</code>文件就好了。如下所示:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/// <reference path = "./test.d.ts" /></span><br><span class="line">function doSomeThing(){</span><br><span class="line"> showmsg('ts');</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">doSomeThing();</span><br></pre></td></tr></table></figure>
<p>在TS文件中引用<code>.d.ts</code>文件的方法是,在文件的开头写上 <code>/// <reference path="./test.d.ts" /></code> 即可。这样当我们使用<code>tsc</code>去编译<code>.ts</code>文件时就不会报错了。</p>
<p>当<code>tsc</code>编译好TS文件后,我们可以在HTML中引入生成的JS文件以及它所使用的JS库了。代码如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><html></span><br><span class="line"> <head></span><br><span class="line"> <title> test ts call js</title></span><br><span class="line"> </head></span><br><span class="line"> <body></span><br><span class="line"> <script src="./test.js"></script></span><br><span class="line"> <script src="./my.js"></script></span><br><span class="line"> </body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure>
<p>此时,在浏览器的<code>debugger</code>里就可以看到<code>the msg is :ts</code>这句话了。</p>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>本文我向你介绍了<code>TypeScript</code>的基本工作原理,从中我们可能知道TypeScript解决了JavaScript最大的问题,即类型检测。但TypeScript并不是一门新语言,它也不会代替JavaScript,它只是JavaScript的一个超集而以。</p>
]]></content>
<categories>
<category>语言</category>
</categories>
<tags>
<tag>TypeScript</tag>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title>VS中引入并使用WebRTC库</title>
<url>/fcd68433/</url>
<content><![CDATA[<p><img data-src="https://avdancevod.oss-cn-beijing.aliyuncs.com/image/article/import_webrtc/webrtc_banner_2.jpeg" alt="banner"></p>
<p>前段时间经常有同学问我,如何在Windows下利用编译出的WebRTC库开发自己的应用程序,当时特别忙就让同学到网上找答案了,没想到过了几天同学们又来问,说网上找不到……</p>
<p>相对于移动端,在Windows下使用WebRTC库确实困难些。在移动端(iOS、Andorid),你可以直接从Google提供的Pod库中拉取编译好的WebRTC库,而在Windows端则需要我们自己编译WebRTC库,并导出WebRTC头文件。</p>
<p>这几天手头的工作终于忙的差不多了,今天就花点时间整理一下这方面的知识,给同学们搭个<strong>台阶</strong>,让同学们快尽入手WebRTC:)。</p>
<span id="more"></span>
<h2 id="安装开发环境"><a href="#安装开发环境" class="headerlink" title="安装开发环境"></a>安装开发环境</h2><p>在Windows下开发应用程序最常用的开发工具还是Visual Studio,你可以使用VS2019,也可以使用VS2022,目前我还是建议大家先用 VS2019,再等个半年、一年的换VS2022比较合适。VS2019的下载地址<a href="https://visualstudio.microsoft.com/zh-hans/thank-you-downloading-visual-studio/?sku=community&rel=16&utm_medium=microsoft&utm_campaign=download+from+relnotes&utm_content=vs2019ga+button">在这里</a>,将 VS Installer下载好后,在CMD窗口中执行下面的命令即可。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ PATH_TO_INSTALLER.EXE ^</span><br><span class="line">--add Microsoft.VisualStudio.Workload.NativeDesktop ^</span><br><span class="line">--add Microsoft.VisualStudio.Component.VC.ATLMFC ^</span><br><span class="line">--includeRecommended</span><br></pre></td></tr></table></figure>
<p>当然,正常情况下你在配置WebRTC编译环境时就应该已经将VS安装好了。</p>
<h2 id="编译WebRTC"><a href="#编译WebRTC" class="headerlink" title="编译WebRTC"></a>编译WebRTC</h2><p>开发环境安装好后,下面我们就该编译WebRTC源码了。WebRTC源码的下载与编译请看<a href="https://avdancedu.com/2bafd6cf/">这篇文章</a>。</p>
<p>需要注意的是,我们在新项目中引入的WebRTC库,不能直接用下面的命令进行编译:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">gn gen out/Default</span><br><span class="line">ninja -C out/Default</span><br></pre></td></tr></table></figure>
<p>而必须明确指明编译出的WebRTC是<strong>Debug</strong>版本,还是<strong>Release</strong>版本;是<strong>x86</strong>版本还是<strong>x64</strong>版本……</p>
<p>因此,应该使用下面的命令编译WebRTC:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">gn gen --target=x64 --ide=vs2019 --args="is_debug=true rtc_enable_protobuf=false is_clang=false target_cpu=\"x64\" enable_iterator_debugging=true use_custom_libcxx=false symbol_level=0 rtc_include_tests=false" out/debug_x64</span><br><span class="line">ninja - C out/debug_x64</span><br></pre></td></tr></table></figure>
<p>上述 <strong>gn</strong> 中的几个参数含义如下:</p>
<ul>
<li>–target,顾名思义,生成x64版本的WebRTC库</li>
<li>–ide,生成VS工程文件</li>
<li>–args,编译时的一些配置参数<ul>
<li>is_debug,为true编译出Debug版本;为false编译出Release版本</li>
<li>rtc_enable_protobuf,是否使用protobuf,使用可将其设置为true</li>
<li>use_custom_libcxx,WebRTC默认使用的是libc++库,而我们在Windows上使用的是libstdc++库,所以需要将其设置为false</li>
<li>symbol_level,编译出的WebRTC库是否带符号表,这个数据量很大,会影响运行速度,所以一般设置为0,表示编译出的WebRTC不带符号表</li>
<li>rtc_include_tests,编译WebRTC时是否编译测试用例,如果为false则不编译,这样可以大大加快WebRTC的编译速度</li>
</ul>
</li>
</ul>
<p>执行上面的命令时,会花一些时间,因此我们需要让<strong>子弹飞一会儿</strong>……</p>
<h2 id="构建自己的应用程序"><a href="#构建自己的应用程序" class="headerlink" title="构建自己的应用程序"></a>构建自己的应用程序</h2><p>如果顺利的话,你现在应该已经将WebRTC库编译好了。接下来我们来创建自己的应用程序。</p>
<p>为了方便,你可以将WebRTC examples中的peerconnection_client代码拿出来构建一个新的工程,之后再将前面编译好的WebRTC库引入进来,<strong>如果它可以正常运行就达到了我们的目标</strong>。</p>
<p>为了达到这个目标,首先我们先使用VS创建一个空项目,步骤如下:</p>
<ul>
<li>第一步,打开Visual Studio,<strong>创建新项目</strong><br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/vs1.png" alt="图1"></li>
<li>第二步,使用Windows桌面向导创建Windows空项目<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/vs2.png" alt="图2"></li>
<li>第三步,填写项目名称,并将项目与解决方案放在同一目录下<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/vs3.png" alt="图3"></li>
<li>第四步,选择应用程序类型为<strong>桌面应用程序</strong><br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/vs4.png" alt="图4"></li>
<li>第五步,同时勾选<strong>空项目</strong><br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/vs5.png" alt="图5"></li>
</ul>
<p>至此,我们就构建出了一个VS<strong>空项目</strong>,它里边没有任何文件,如下图所示:</p>
<p><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/vs6_new.png" alt="图6"></p>
<p>空项目创建好后,紧接着我们来移植peerconnection_client代码到新项目中,步骤如下:</p>
<ul>
<li><p>第一步,从WebRTC源码中拷贝peerconnection_client中的代码到新项目的目录中,在我这里是<br>将<code>C:\webrtc\webrtc-checkout\src\exmaples\peerconnection\client</code>目录中的代码拷贝到<code>C:\Users\lichao\sourceMyWebRTCDemo</code>目录下。如下图所示:</p>
<p><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/code1.png" alt="code1"></p>
<p><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/code2.png" alt="code2"></p>
</li>
<li><p>第二步,将新项目中的代码<strong>拖</strong>到VS项目中<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/code3_new.png" alt="code3"></p>
</li>
</ul>
<p>通过以上步骤我们就将peerconnection_client中的代码移植好了。接下来咱们来看<strong>重头戏</strong>,如何在项目中引入WebRTC库。</p>
<h2 id="引入WebRTC库"><a href="#引入WebRTC库" class="headerlink" title="引入WebRTC库"></a>引入WebRTC库</h2><p>通常我们引入一个外部库只需要两步,<strong>引入库文件和其头文件</strong>。不过,对于WebRTC,更准确的说对于peerconnection_client而言,它需要的不仅仅是WebRTC库,还需要将WebRTC依赖的第三方库加进来,这是大家觉得在Windows下使用WebRTC库比较麻烦的原因。</p>
<p>下面咱们就来看一下如何引入WebRTC库吧!</p>
<h3 id="添加依赖的头文件"><a href="#添加依赖的头文件" class="headerlink" title="添加依赖的头文件"></a>添加依赖的头文件</h3><p>我们若想将WebRTC头文件引入到项目中,可以通过下面两种方法引入:</p>
<ul>
<li><p>方法一,在VS中将WebRTC源码路径添加到<strong>附加包含目录</strong>中。比如我这里将WebRTC源码下载到了<code>C:\webrtc\webrtc-checkout\src</code>目录下,我只需将该路径添加到:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">项目 -> 属性 -> C/C++ -> 常规 -> 附加包含目录</span><br></pre></td></tr></table></figure>
<p>中即可,如下图所示:<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/setting1.png" alt="setting1"></p>
<p><strong>这种方法的好处是简单方便,坏处是不便于我们将库文件发布给别人使用。</strong></p>
</li>
<li><p>方法二,我们可以通过<a href="https://avdancevod.oss-cn-beijing.aliyuncs.com/image/article/import_webrtc/extrac_webrtc_headers.bat">这个脚本</a>将WebRTC中的头文件提取出来。之后与<strong>方法一</strong>一样,将头文件路径添加到<strong>附加包含目录</strong>中即可。</p>
<p>需要注意的是,这个脚本下载后,要将其放到WebRTC源码目录<strong>src</strong>的同级目录中,如下图所示:<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/script.png" alt="script1"></p>
<p>之后打开Windows控制台,并进入到<strong>src</strong>的同级目录中,在CMD窗口中执行<code>extract_webrtc_headers.bat</code>脚本,这样就可以将WebRTC头文件提取出来了,如下图所示:<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/script2.png" alt="script2"></p>
<p><strong>这种方法的优点是方便其他人使用,缺点是抽取头文件需要花一些时间。</strong></p>
</li>
</ul>
<p>除了添加上面的头文件路径外,我们还需要将下面几个路径添加到<strong>附加包含项目</strong>中:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">C:\webrtc\webrtc-checkout\src\third_party\jsoncpp\generated</span><br><span class="line">C:\webrtc\webrtc-checkout\src\third_party\jsoncpp\source\include</span><br><span class="line">C:\webrtc\webrtc-checkout\src\third_party\libyuv\include</span><br><span class="line">C:\webrtc\webrtc-checkout\src\third_party\abseil-cpp</span><br></pre></td></tr></table></figure>
<h3 id="添加依赖的库"><a href="#添加依赖的库" class="headerlink" title="添加依赖的库"></a>添加依赖的库</h3><p>头文件添加好后,接下来咱们来添加WebRTC库文件。WebRTC编译好后,你可以在WebRTC源码目录<strong>src</strong>的<strong>out/debug_x64/obj</strong>目录下找到<strong>WebRTC.lib</strong>文件,这就是编译好的WebRTC库。</p>
<p>我们将它添加到VS中的<strong>附加库目录</strong>中,具体操作如下:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">右键项目 -> 属性 -> 链接器 -> 常规 -> 附加库目录</span><br></pre></td></tr></table></figure>
<p>WebRTC库文件路径添加好后,如下图所示:<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/linker1.png" alt="linker1"></p>
<p>接着咱们添加具体的的依赖库,添加依赖库的位置在:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">右键项目 -> 属性 -> 链接器 -> 输入 -> 附加依赖项</span><br></pre></td></tr></table></figure>
<p>如下图所示:<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/linker2.png" alt="linker2"></p>
<p>具体都依赖哪些依赖项呢?这里我以 <strong>M93(4577)</strong> 为例,对于这个版本的peerconnection_client来说,它需要下面的依赖库:</p>
<ul>
<li><p>WebRTC相关的库包括:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">third_party/abseil-cpp/absl/flags/marshalling/marshalling.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/program_name/program_name.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/flag/flag.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/flag_internal/flag.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/commandlineflag/commandlineflag.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/commandlineflag_internal/commandlineflag.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/private_handle_accessor/private_handle_accessor.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/reflection/reflection.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/parse/parse.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/usage/usage.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/usage_internal/usage.obj</span><br><span class="line">third_party/abseil-cpp/absl/flags/config/usage_config.obj</span><br><span class="line">third_party/jsoncpp/jsoncpp/json_reader.obj</span><br><span class="line">third_party/jsoncpp/jsoncpp/json_value.obj</span><br><span class="line">third_party/jsoncpp/jsoncpp/json_writer.obj</span><br><span class="line">test/field_trial/field_trial.obj</span><br><span class="line">test/video_test_common/test_video_capturer.obj</span><br><span class="line">test/platform_video_capturer/vcm_capturer.obj</span><br><span class="line">rtc_base/rtc_json/json.obj</span><br></pre></td></tr></table></figure>
</li>
<li><p>系统相关的库包括:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">advapi32.lib</span><br><span class="line">comdlg32.lib</span><br><span class="line">dbghelp.lib</span><br><span class="line">dnsapi.lib</span><br><span class="line">gdi32.lib</span><br><span class="line">msimg32.lib</span><br><span class="line">odbc32.lib</span><br><span class="line">odbccp32.lib</span><br><span class="line">oleaut32.lib</span><br><span class="line">shell32.lib</span><br><span class="line">shlwapi.lib</span><br><span class="line">user32.lib</span><br><span class="line">usp10.lib</span><br><span class="line">uuid.lib</span><br><span class="line">version.lib</span><br><span class="line">wininet.lib</span><br><span class="line">winmm.lib</span><br><span class="line">winspool.lib</span><br><span class="line">ws2_32.lib</span><br><span class="line">delayimp.lib</span><br><span class="line">kernel32.lib</span><br><span class="line">ole32.lib</span><br><span class="line">crypt32.lib</span><br><span class="line">iphlpapi.lib</span><br><span class="line">secur32.lib</span><br><span class="line">dmoguids.lib</span><br><span class="line">wmcodecdspuuid.lib</span><br><span class="line">amstrmid.lib</span><br><span class="line">msdmo.lib</span><br><span class="line">strmiids.lib</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="添加宏"><a href="#添加宏" class="headerlink" title="添加宏"></a>添加宏</h3><p>除了上面讲的需要引入头文件和WebRTC库之外,还需要添加下面这些宏。这些宏的具体含义我有不介绍了,有兴趣的同学可以自己在网上搜索一下。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">USE_AURA=<span class="number">1</span></span><br><span class="line">_HAS_NODISCARD</span><br><span class="line">_HAS_EXCEPTIONS=<span class="number">0</span></span><br><span class="line">__STD_C</span><br><span class="line">_CRT_RAND_S</span><br><span class="line">_CRT_SECURE_NO_DEPRECATE</span><br><span class="line">_SCL_SECURE_NO_DEPRECATE</span><br><span class="line">_ATL_NO_OPENGL</span><br><span class="line">_WINDOWS</span><br><span class="line">CERT_CHAIN_PARA_HAS_EXTRA_FIELDS</span><br><span class="line">PSAPI_VERSION=<span class="number">2</span></span><br><span class="line">WIN32</span><br><span class="line">_SECURE_ATL</span><br><span class="line">WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP</span><br><span class="line">WIN32_LEAN_AND_MEAN</span><br><span class="line">NOMINMAX</span><br><span class="line">_UNICODE</span><br><span class="line">UNICODE</span><br><span class="line">NTDDI_VERSION=NTDDI_WIN10_VB</span><br><span class="line">_WIN32_WINNT=<span class="number">0x0A00</span></span><br><span class="line">WINVER=<span class="number">0x0A00</span></span><br><span class="line">_DEBUG</span><br><span class="line">DYNAMIC_ANNOTATIONS_ENABLED=<span class="number">1</span></span><br><span class="line">WEBRTC_ENABLE_PROTOBUF=<span class="number">0</span></span><br><span class="line">WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE</span><br><span class="line">RTC_ENABLE_VP9</span><br><span class="line">WEBRTC_HAVE_SCTP</span><br><span class="line">WEBRTC_ENABLE_AVX2</span><br><span class="line">RTC_ENABLE_WIN_WGC</span><br><span class="line">WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=<span class="number">0</span></span><br><span class="line">WEBRTC_WIN</span><br><span class="line">ABSL_ALLOCATOR_NOTHROW=<span class="number">1</span></span><br><span class="line">_ENABLE_EXTENDED_ALIGNED_STORAGE</span><br><span class="line">ABSL_FLAGS_STRIP_NAMES=<span class="number">0</span></span><br><span class="line">HAVE_WEBRTC_VIDEO</span><br></pre></td></tr></table></figure>
<p>添加方法如下:</p>
<ul>
<li><p>首先在 VS 中执行下面的操作,</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">右键项目 -> 属性 -> C/C++ -> 预处理器</span><br></pre></td></tr></table></figure>
</li>
<li><p>之后将上面的宏添加到<strong>预处理器</strong>中即可。</p>
</li>
</ul>
<h2 id="编译运行"><a href="#编译运行" class="headerlink" title="编译运行"></a>编译运行</h2><p>到此为止,我们就将peerconnection_client的代码移植好了,直接点<img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/run.png">测试一下吧!</p>
<p>此时,编译器有可能报4996的错误,解决该问题办法很简单,只要在</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">右键项目项 -> 属性 -> C/C++ -> 高级 -> 禁用特定警告</span><br></pre></td></tr></table></figure>
<p>中将 <strong>4996</strong> 添加进去即可。除此之外,还有可能遇到 <strong>/MDd</strong> 错误,解决它可以通过在</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">右键项目项 -> 属性 -> C/C++ -> 代码生成 -> 运行库</span><br></pre></td></tr></table></figure>
<p>中将 <strong>/MDd</strong> 改为 <strong>/MTd</strong> 即可。</p>
<p>如果一切顺利,peerconnection_client的连接窗口就展示在你面前了,如下图所示。<br><img data-src="https://cdn.avdancedu.com/image/article/import_webrtc/conn.png"></p>
<p>此时,你需要先将peerconnection_server程序运行起来,让它侦听 <strong>8888</strong> 端口;之后在peerconnection_client的连接界面中输入 <strong>127.0.0.1</strong>,点击<strong>连接</strong>,如果能进入列表界面就表明OK了。</p>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>上面我详细的向你介绍了如何将WebRTC中的peerconnection_client代码移植出来,并重点向你讲解了如何在项目中引入WebRTC库。</p>
<p>其重点包括三步:一是添加WebRTC库头文件路径;二是添加WebRTC库路径,及其WebRTC库;三是添加一堆宏。只要将上面三个步骤操作好了,其它的一些问题在网上都能找到答案。</p>
<p>祝大家好运!</p>
]]></content>
<categories>
<category>WebRTC</category>
</categories>
<tags>
<tag>WebRTC</tag>
<tag>VS</tag>
</tags>
</entry>
<entry>
<title>SDL音频播放器的实现</title>
<url>/f94132a0/</url>
<content><![CDATA[<p>今天向大家介绍一下如何通过 SDL 实现一个PCM音频播放器。这是一个最简单的播放器,它不涉及到音频的解复用,解码等工作。我们只需要将音频原始数据喂给 SDL 音频接口就可以听到悦耳的声音了。在下面的列子中我将向你演示,使用 SDL 做这样一个播放器是何等的简单。</p>
<p>当然这个看似简单的播放器其实是由许多的理论基础在底层支持着的。如果在这方面没有什么基础的同学可以通过下面的链接去自行学习。</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
</ul>
<span id="more"></span>
<h2 id="播放音频的基本原则"><a href="#播放音频的基本原则" class="headerlink" title="播放音频的基本原则"></a>播放音频的基本原则</h2><p>如果我们要播放一段声音,想当然的认为直接将播放的声音发送给声卡,这样扬声器就会将声音播放出来。只要我们不断的送数据,声音就会不停的输出。</p>
<p>事实上真的是这样吗?<strong>当 然 不 是!!!</strong></p>
<p>实际上,所有的音频播放都遵守着一个原则,就是当声卡将要播放的声音输出到扬声器时,它首先会通过回调函数,向你要它一部分声频数据,然后拿着这部分音频数据去播放。等播放完了,它会再向你要下一部分。</p>
<p>至于要的数据的多少,什么时候向你要,这些都是由声卡决定的。对于我们上层应用来说,这些都是由底层 API 决定的。</p>
<p>为什么会出现这种情况呢?为什么播放音频与我们一般的逻辑相反呢?这是因为声卡会严格按照音频的播放时间进行播放,不会多一秒,也不会少一秒。正因为它能准确的计算出时间来,而应用层是不知道这个时间的,所以我们必须按照声卡的要求给它喂数据,而不能依据自己的性子来。</p>
<p>那么有人会问,为什么声卡可以精准的计算出播放时间来呢?这是因为在播放之前我们给它设置了采样率、通道数、采样大小等参数,通过这些参数它就可以计算出时间来。</p>
<p>我们来做个计算,假设采样率是 48000, 双通道,采样大小是 16bit,那么一秒种的数据是多少呢? 48000*2*16=1536000. 反过来,如果我们有一段 8M 的数据,那么声卡就知道它能播放 5秒多的声音。</p>
<p>上面的一大段文字描述,实际上只是想说明一个道理,就是要播放的声音数据,是声卡主动要的,不能由上层直接设置。这是通过回调函数来实现的。后面会有具体的例子。</p>
<h2 id="SDL如何处理音频"><a href="#SDL如何处理音频" class="headerlink" title="SDL如何处理音频"></a>SDL如何处理音频</h2><p>SDL是一个处理多媒体的开源库,我们来看看它是如何播放音频的,具体的操作步骤是啥?</p>
<ul>
<li>打开音频设备</li>
<li>设置音频参数</li>
<li>播放音频</li>
<li>向声卡喂数据</li>
<li>关闭音频设置</li>
</ul>
<h2 id="详细API介绍"><a href="#详细API介绍" class="headerlink" title="详细API介绍"></a>详细API介绍</h2><ul>
<li><p>打开音频设备</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int SDL_OpenAudio(SDL_AudioSpec* desired,</span><br><span class="line"> SDL_AudioSpec* obtained)</span><br></pre></td></tr></table></figure>
<ul>
<li><p>desired: 设置音频参数。</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>freq</td>
<td>每秒采频率</td>
</tr>
<tr>
<td>SDL_AudioFormat</td>
<td>音频数据存储格式</td>
</tr>
<tr>
<td>channels</td>
<td>通道数</td>
</tr>
<tr>
<td>silence</td>
<td>静音值</td>
</tr>
<tr>
<td>samples</td>
<td>采样个数</td>
</tr>
<tr>
<td>size</td>
<td>音频缓冲区大小</td>
</tr>
<tr>
<td>SDL_AudioCallback</td>
<td>回调函数</td>
</tr>
<tr>
<td>userdata</td>
<td>回调函数参数指针</td>
</tr>
</tbody></table>
</li>
<li><p>obtained: 返回参数。</p>
</li>
</ul>
</li>
<li><p>关闭音频设备</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_CloseAudio(void)</span><br></pre></td></tr></table></figure>
</li>
<li><p>播放与暂停</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_PauseAudio(int pause_on)</span><br></pre></td></tr></table></figure>
<p>pause_on: 0, 暂停播放;1, 播放;</p>
</li>
<li><p>喂数据</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_MixAudio(Uint8* dst,</span><br><span class="line"> const Uint8* src,</span><br><span class="line"> Uint32 len,</span><br><span class="line"> int volume)</span><br></pre></td></tr></table></figure>
<ul>
<li>dst: 目的缓冲区</li>
<li>src: 源缓冲区</li>
<li>len: 音频数据长度</li>
<li>volume: 音量大小,0-128 之间的数。SDL_MIX_MAXVOLUME代表最大音量。</li>
</ul>
</li>
</ul>
<h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><p>这个例子主要为大家展示了一下如何使用 SDL 的音频 API 来播放声音。其基本流程是,从 pcm 文件一块一块的读数据。然后通过 read_audio_data 这个回调函数给声卡喂数据。如果一次没用完,SDL会再次调用回调函数读数据。</p>
<p>如果audio_buf中的数据用完了,则再次从文件中读一块数据,直到读到文件尾。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">#include <stdio.h></span><br><span class="line">#include <SDL.h></span><br><span class="line"></span><br><span class="line">#define BLOCK_SIZE 4096000</span><br><span class="line"></span><br><span class="line">static Uint8 *audio_buf = NULL;</span><br><span class="line">static Uint8 *audio_pos = NULL;</span><br><span class="line">static size_t buffer_len = 0;</span><br><span class="line"></span><br><span class="line">//callback function for audio devcie</span><br><span class="line">void read_audio_data(void *udata, Uint8 *stream, int len){</span><br><span class="line"></span><br><span class="line"> if(buffer_len == 0){</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> SDL_memset(stream, 0, len);</span><br><span class="line"></span><br><span class="line"> len = (len < buffer_len) ? len : buffer_len;</span><br><span class="line"> SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);</span><br><span class="line"></span><br><span class="line"> audio_pos += len;</span><br><span class="line"> buffer_len -= len;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">int main(int argc, char *argv[])</span><br><span class="line">{</span><br><span class="line"> int ret = -1;</span><br><span class="line"></span><br><span class="line"> FILE *audio_fd = NULL;</span><br><span class="line"></span><br><span class="line"> SDL_AudioSpec spec;</span><br><span class="line"></span><br><span class="line"> char *path = "./test.pcm";</span><br><span class="line"></span><br><span class="line"> //SDL initialize</span><br><span class="line"> if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){</span><br><span class="line"> fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());</span><br><span class="line"> return ret;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //open pcm file</span><br><span class="line"> audio_fd = fopen(path, "r");</span><br><span class="line"> if(!audio_fd){</span><br><span class="line"> fprintf(stderr, "Failed to open pcm file!\n");</span><br><span class="line"> goto __FAIL;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">//SDL_AudioSpec</span><br><span class="line"> spec.freq = 44100;;</span><br><span class="line"> spec.format = AUDIO_S16SYS;</span><br><span class="line"> spec.channels = 2;</span><br><span class="line"> spec.silence = 0;</span><br><span class="line"> spec.samples = 1024;;</span><br><span class="line"> spec.callback = read_audio_data;;</span><br><span class="line"> spec.userdata = NULL;</span><br><span class="line"></span><br><span class="line"> //open audio devcie</span><br><span class="line"> if(SDL_OpenAudio(&spec, NULL)){</span><br><span class="line"> fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());</span><br><span class="line"> goto __FAIL;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //play audio</span><br><span class="line"> SDL_PauseAudio(0);</span><br><span class="line"></span><br><span class="line"> do{</span><br><span class="line"> //read data from pcm file</span><br><span class="line"> buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);</span><br><span class="line"> fprintf(stderr, "block size is %zu\n", buffer_len);</span><br><span class="line"></span><br><span class="line"> audio_pos = audio_buf;</span><br><span class="line"></span><br><span class="line"> //the main thread wait for a moment</span><br><span class="line"> while(audio_pos < (audio_buf + buffer_len)) {</span><br><span class="line"> SDL_Delay(1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }while(buffer_len !=0);</span><br><span class="line"></span><br><span class="line"> //close audio device</span><br><span class="line"> SDL_CloseAudio();</span><br><span class="line"></span><br><span class="line"> ret = 0;</span><br><span class="line"></span><br><span class="line">__FAIL:</span><br><span class="line"> //release some resources</span><br><span class="line"> if(audio_buf){</span><br><span class="line"> free(audio_buf);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if(audio_fd){</span><br><span class="line"> fclose(audio_fd);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> //quit SDL</span><br><span class="line"> SDL_Quit();</span><br><span class="line"></span><br><span class="line"> return ret;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>本文向大家讲解了一下如何通过SDL库的音频处理 API 实现一个最简单的 PCM 播放器。通过个例子大家可以了解到,SDL的使用是如此简单。</p>
<p>当然这个播放器还是有点 Low,不过不要紧,随着后面文章的推出,你会逐渐看到一个完整的播放器是如何被打造出来的。</p>
<p>希望本文能对你有所帮助,谢谢!</p>
<h2 id="隆重推荐"><a href="#隆重推荐" class="headerlink" title="隆重推荐"></a>隆重推荐</h2><ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
<li><a href="https://coding.imooc.com/class/279.html">ffmpeg精讲</a></li>
</ul>
]]></content>
<categories>
<category>视频渲染</category>
</categories>
<tags>
<tag>SDL</tag>
</tags>
</entry>
<entry>
<title>SDL多线程</title>
<url>/a6aca2fe/</url>
<content><![CDATA[<p>今天将向大家介绍一下SDL中的多线程的使用。通过下面对SDL 线程与锁相关的API介绍,你会发现,它与 Linux, Windows相关的API几乎是一模一样的。从这里可以推断出,其实SDL对于多线程的处理只是为大家提供了一套统一接口,并没有做其它太多的工作。</p>
<p>这是我们介绍 SDL 的第六篇文章。有兴趣的同学可以通过下面的链接查看其它几篇文章。</p>
<ul>
<li><a href="http://www.avdancedu.com/56ef4bcb">SDL 入门</a></li>
<li><a href="http://www.avdancedu.com/287ad9ab">SDL窗口渲染</a></li>
<li><a href="http://www.avdancedu.com/24ee78a8">SDL基本图形绘制</a></li>
<li><a href="http://www.avdancedu.com/a0ec02a7">SDL事件处事</a></li>
<li><a href="http://www.avdancedu.com/67189745">彻底理解SDL纹理</a></li>
<li><a href="http://www.avdancedu.com/a6aca2fe">SDL孙悟空与多线程</a></li>
<li><a href="http://www.avdancedu.com/f94132a0">PCM音频播放器的实现</a></li>
<li><a href="https://coding.imooc.com/class/279.html">ffmpeg精讲</a></li>
</ul>
<span id="more"></span>
<h2 id="为啥要用多线程?"><a href="#为啥要用多线程?" class="headerlink" title="为啥要用多线程?"></a>为啥要用多线程?</h2><p>我觉得这个小节的标题就是一个废话。不过为了文章的完整性,还是简单的说一说吧。多线程(多进程)是啥意思呢?做个不恰当的比喻,可以把CPU看成是孙悟空,它有一个能耐,从后脑揪几个猴毛就可以变出许多的小猴子。</p>
<p>多线程(多进程)就是这些小猴子。当干一件比较复杂的事儿时,可以孙悟空一个人干,这样自己比较累。它还有一种选择就是揪几根猴毛,让小猴子们一起帮着干。这样一件复杂的事件,分给许多猴子干,每只猴只干一部分,事情很快就被做完了,这样岂不是比一个人干要强的多?</p>
<p>当然,有好处也有坏外。猴子多了就需要管理,如果管理不好,就会闹翻天。比如,只有一块肉,该给哪个猴子吃呢?这真是一个另人头痛的问题。</p>
<p>实际上整个操作系统的演进,就是一部管理学的演进。如何才能让CPU,内存,磁盘I/O,各种设备之间高效的工作,一直是操作系统追求的目标。当然,这话有点扯远了。</p>
<p>今天我们要讲的就是多线程(多进程)之间该如何高效的工作。要想让多线程之间高效工作,就要给它们之间立点规矩,大家都要遵守的规矩。</p>
<h2 id="线程互斥与同步"><a href="#线程互斥与同步" class="headerlink" title="线程互斥与同步"></a>线程互斥与同步</h2><p>当僧多粥少时,就引入了互斥的概念。再举个我们生活中的例子吧,比如有一大家族住在同一个大屋子里,却只有一个厕所。早上起来大家都想去厕所,这时有谁先抢到了厕所,其它人就只能等他出来后再进入了,这就是<strong>互斥</strong>。</p>
<p>当仅有一份资源,大家都需要时,这就产生了管理问题。解决的办法就是通过互斥方法来解决。这种情况是在做多线程处理时要尽量避免的;如果资源足够呢?那当然是平均分配,人人有份了。这中情况是多线路程最希望的。</p>
<p>除了互斥之外,有些情况还需要更精细化的管理,比如说<strong>同步</strong>。例如车间里的流水线,每个人负责一块,每一块都是半成品,第一个人完成之后交给第二个人做下一步,而后面的人又必须依赖于前而人的结果,依次类推,最后一个人才能完成最终的产品。这就是线程间的精细化管理<strong>同步</strong>。</p>
<p>要想实现互斥和同步,就需要一种机制。在操作系统上提供了锁的概念来达到互斥与同步。</p>
<h2 id="锁的种类"><a href="#锁的种类" class="headerlink" title="锁的种类"></a>锁的种类</h2><p>在操作系统上有很种锁,有读写锁、自旋锁、可重入锁等。下面我简单的介绍一下它们之间的不同。</p>
<p>读写锁: 分为读锁与写锁。所谓读锁就是被访问的资源只要你不改变它的值,你就可以访问,但如果你想改变它,那么就需要等所有读它的线程都释放了它们的锁后,才可以进行修改;写锁是同一时刻只能有一个人访问,当资源被加锁后,其它人只能等待。</p>
<p>自旋锁: 偿试着给访问资源加锁,如果此时被访问资源已经上锁了,那就一直不停的偿试,直到加锁成功为止。由于它会非常消耗CPU资源,所以一般只锁今资源非常短的情况下才能使用它。</p>
<p>可重入锁: 同一个线程对被访问资源可以一直加锁。但如果被访问资源已经上锁了,那么其它线程则无法对其加锁。</p>
<p>锁是解决互斥的一种好办法,但同样有利必有弊。如果使用不善就会出现死锁。</p>
<h2 id="死锁问题"><a href="#死锁问题" class="headerlink" title="死锁问题"></a>死锁问题</h2><p>死锁顾名思意,就是打不开的锁。它是怎么产生的呢?举个例子,两个人需要一起完成一件事儿,A说他要等B做完了,他才能开始;而B说它要等A做完了,它才能开使。于时他们在相互等待中老去。</p>
<p>看类很简单的问题,但这类事情经常在我们的工作中出现。而在我们开发的多线程程序中更是频繁出现。别说人没遇到过哟!</p>
<p>如何解决?那就是考验你的管理能力了。共实很多情况是出现了死锁我们自己却不知道,否则的话,凭我们的聪名才智怎么能让他们一直锁在那儿呢。</p>
<h2 id="SDL多线程"><a href="#SDL多线程" class="headerlink" title="SDL多线程"></a>SDL多线程</h2><p>上面介绍了一大堆的理论,现在来看看 SDL 为我们都提供了那些API吧。</p>
<ul>
<li><p>创建线程</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">SDL_Thread* SDL_CreateThread(SDL_ThreadFunction fn,</span><br><span class="line"> const char* name,</span><br><span class="line"> void* data)</span><br></pre></td></tr></table></figure>
<ul>
<li>fn: 线程要运行的函数。</li>
<li>name: 线程名。</li>
<li>data: 函数参数。</li>
</ul>
</li>
<li><p>等待线程</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">void SDL_WaitThread(SDL_Thread* thread,</span><br><span class="line"> int* status)</span><br></pre></td></tr></table></figure>
<p>等待线程结束。</p>