-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
512 lines (275 loc) · 292 KB
/
atom.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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>大道废</title>
<subtitle>一切有为法,如梦幻泡影,如露亦如电,当作如是观.</subtitle>
<link href="http://horsefaced.github.io/atom.xml" rel="self"/>
<link href="http://horsefaced.github.io/"/>
<updated>2024-08-18T03:29:33.491Z</updated>
<id>http://horsefaced.github.io/</id>
<author>
<name>马面</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>2023年梅里南坡装备</title>
<link href="http://horsefaced.github.io/2023/09/22/2023%E5%B9%B4%E6%A2%85%E9%87%8C%E5%8D%97%E5%9D%A1%E8%A3%85%E5%A4%87/"/>
<id>http://horsefaced.github.io/2023/09/22/2023%E5%B9%B4%E6%A2%85%E9%87%8C%E5%8D%97%E5%9D%A1%E8%A3%85%E5%A4%87/</id>
<published>2023-09-22T06:48:04.000Z</published>
<updated>2024-08-18T03:29:33.491Z</updated>
<content type="html"><![CDATA[<hr><table><thead><tr><th><strong>类别</strong></th><th><strong>名称</strong></th><th><strong>数量</strong></th><th><strong>重量</strong></th><th><strong>Check</strong></th><th></th></tr></thead><tbody><tr><td>三大件</td><td>田野睡袋</td><td>1</td><td>872</td><td>OK</td><td></td></tr><tr><td></td><td>therm-a-rest xlite+气泵</td><td>1</td><td>415</td><td>OK</td><td></td></tr><tr><td></td><td>AMK Bivy 地布</td><td>1</td><td>81</td><td>OK</td><td></td></tr><tr><td></td><td>山鹰户外帐篷</td><td>1</td><td>430</td><td>OK</td><td></td></tr><tr><td></td><td>hmg2400</td><td>1</td><td>1010</td><td>OK</td><td></td></tr><tr><td></td><td>钛空心地丁</td><td>10</td><td>100</td><td>OK</td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td>2908</td></tr><tr><td>炊具</td><td>火枫大黄蜂</td><td>1</td><td>40</td><td>OK</td><td></td></tr><tr><td></td><td>千橡树550ml锅</td><td>1</td><td>72</td><td>OK</td><td></td></tr><tr><td></td><td>purewell净水器</td><td>1</td><td>73</td><td>OK</td><td></td></tr><tr><td></td><td>2升鸭嘴兽</td><td>1</td><td>39</td><td>OK</td><td></td></tr><tr><td></td><td>勺子</td><td>1</td><td>2</td><td>OK</td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td>226</td></tr><tr><td>衣服</td><td>smartwood袜子</td><td>1</td><td>68</td><td></td><td></td></tr><tr><td></td><td>速干长裤</td><td>1</td><td>320</td><td></td><td></td></tr><tr><td></td><td>三峰防雨裙</td><td>1</td><td>69</td><td>OK</td><td></td></tr><tr><td></td><td>老鼠神衣</td><td>1</td><td>275</td><td>OK</td><td></td></tr><tr><td></td><td>始祖鸟冲锋衣</td><td>1</td><td>360</td><td>OK</td><td></td></tr><tr><td></td><td>羽绒服</td><td>1</td><td>330</td><td>OK</td><td></td></tr><tr><td></td><td>羊毛帽子</td><td>1</td><td>61</td><td>OK</td><td></td></tr><tr><td></td><td>buff太阳帽</td><td>1</td><td>30</td><td></td><td></td></tr><tr><td></td><td>羊毛内衣</td><td>1</td><td>205</td><td>OK</td><td></td></tr><tr><td></td><td>内裤</td><td>1</td><td>43</td><td>OK</td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td>1761</td></tr><tr><td>电器</td><td>anker一万毫安</td><td>1</td><td>203</td><td>OK</td><td></td></tr><tr><td></td><td>Apple Watch充电器</td><td>1</td><td>19</td><td>OK</td><td></td></tr><tr><td></td><td>紫米Type-C充电线+Lightning转接头+microUSB转接头</td><td>1</td><td>18</td><td>OK</td><td></td></tr><tr><td></td><td>Apple Watch 5</td><td>1</td><td>46</td><td>OK</td><td></td></tr><tr><td></td><td>iPhone 13 pro</td><td>1</td><td>205</td><td>OK</td><td></td></tr><tr><td></td><td>充电器</td><td>1</td><td>46</td><td>OK</td><td></td></tr><tr><td></td><td>头灯</td><td>1</td><td>40</td><td>OK</td><td></td></tr><tr><td></td><td>伏来阳太阳能板</td><td>1</td><td>195</td><td>OK</td><td></td></tr><tr><td></td><td>收纳袋</td><td>1</td><td>12</td><td>OK</td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td>784</td></tr><tr><td>杂项</td><td>牙刷牙膏毛巾耳塞</td><td>1</td><td>55</td><td>OK</td><td></td></tr><tr><td></td><td>眼镜</td><td>1</td><td>20</td><td>OK</td><td></td></tr><tr><td></td><td>药品</td><td>1</td><td>26</td><td>OK</td><td></td></tr><tr><td></td><td>迪卡侬雨伞</td><td>1</td><td>154</td><td>OK</td><td></td></tr><tr><td></td><td></td><td></td><td>5934</td><td></td><td>255</td></tr></tbody></table><p>带的食物:</p><p><img src="/images/IMG_4469.png" alt="IMG_4469"></p>]]></content>
<summary type="html"><hr>
<table>
<thead>
<tr>
<th><strong>类别</strong></th>
<th><strong>名称</strong></th>
<th><strong>数量</strong></th>
<th><strong>重量</strong></th</summary>
<category term="户外 装备" scheme="http://horsefaced.github.io/tags/%E6%88%B7%E5%A4%96-%E8%A3%85%E5%A4%87/"/>
</entry>
<entry>
<title>ffmpeg与rtsp源代码分析</title>
<link href="http://horsefaced.github.io/2022/03/10/ffmpeg%E4%B8%8Ertsp%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90/"/>
<id>http://horsefaced.github.io/2022/03/10/ffmpeg%E4%B8%8Ertsp%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90/</id>
<published>2022-03-10T01:57:22.000Z</published>
<updated>2022-04-08T10:36:03.112Z</updated>
<content type="html"><![CDATA[<h2 id="分析的源码"><a href="#分析的源码" class="headerlink" title="分析的源码"></a>分析的源码</h2><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><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><fstream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><sstream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span></span><br><span class="line">{</span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><libavcodec/avcodec.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><libavformat/avformat.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><libavformat/avio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><libswscale/swscale.h></span></span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Open the initial context variables that are needed</span></span><br><span class="line"> SwsContext *img_convert_ctx;</span><br><span class="line"> AVFormatContext *format_ctx = avformat_alloc_context();</span><br><span class="line"> AVCodecContext *codec_ctx = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">int</span> video_stream_index;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Register everything</span></span><br><span class="line"> <span class="comment">//av_register_all();</span></span><br><span class="line"> <span class="comment">// avformat_network_init();</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// open RTSP</span></span><br><span class="line"> <span class="keyword">if</span> (avformat_open_input(&format_ctx, <span class="string">"rtsp://134.169.178.187:8554/h264.3gp"</span>,</span><br><span class="line"> <span class="literal">NULL</span>, <span class="literal">NULL</span>) != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> EXIT_FAILURE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (avformat_find_stream_info(format_ctx, <span class="literal">NULL</span>) < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> EXIT_FAILURE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// search video stream</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < format_ctx->nb_streams; i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)</span><br><span class="line"> video_stream_index = i;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> AVPacket packet;</span><br><span class="line"> av_init_packet(&packet);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// open output file</span></span><br><span class="line"> AVFormatContext *output_ctx = avformat_alloc_context();</span><br><span class="line"></span><br><span class="line"> AVStream *stream = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// start reading packets from stream and write them to file</span></span><br><span class="line"> av_read_play(format_ctx); <span class="comment">// play RTSP</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// Get the codec</span></span><br><span class="line"> AVCodec *codec = <span class="literal">NULL</span>;</span><br><span class="line"> codec = avcodec_find_decoder(AV_CODEC_ID_H264);</span><br><span class="line"> <span class="keyword">if</span> (!codec)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Add this to allocate the context by codec</span></span><br><span class="line"> codec_ctx = avcodec_alloc_context3(codec);</span><br><span class="line"></span><br><span class="line"> avcodec_get_context_defaults3(codec_ctx, codec);</span><br><span class="line"> avcodec_copy_context(codec_ctx, format_ctx->streams[video_stream_index]->codec);</span><br><span class="line"> <span class="built_in">std</span>::ofstream output_file;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (avcodec_open2(codec_ctx, codec, <span class="literal">NULL</span>) < <span class="number">0</span>)</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> img_convert_ctx = sws_getContext(codec_ctx-><span class="built_in">width</span>, codec_ctx-><span class="built_in">height</span>,</span><br><span class="line"> codec_ctx->pix_fmt, codec_ctx-><span class="built_in">width</span>, codec_ctx-><span class="built_in">height</span>, AV_PIX_FMT_RGB24,</span><br><span class="line"> SWS_BICUBIC, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> <span class="built_in">size</span> = avpicture_get_size(AV_PIX_FMT_YUV420P, codec_ctx-><span class="built_in">width</span>,</span><br><span class="line"> codec_ctx-><span class="built_in">height</span>);</span><br><span class="line"> <span class="keyword">uint8_t</span> *picture_buffer = (<span class="keyword">uint8_t</span> *)(av_malloc(<span class="built_in">size</span>));</span><br><span class="line"> AVFrame *picture = av_frame_alloc();</span><br><span class="line"> AVFrame *picture_rgb = av_frame_alloc();</span><br><span class="line"> <span class="keyword">int</span> size2 = avpicture_get_size(AV_PIX_FMT_RGB24, codec_ctx-><span class="built_in">width</span>,</span><br><span class="line"> codec_ctx-><span class="built_in">height</span>);</span><br><span class="line"> <span class="keyword">uint8_t</span> *picture_buffer_2 = (<span class="keyword">uint8_t</span> *)(av_malloc(size2));</span><br><span class="line"> avpicture_fill((AVPicture *)picture, picture_buffer, AV_PIX_FMT_YUV420P,</span><br><span class="line"> codec_ctx-><span class="built_in">width</span>, codec_ctx-><span class="built_in">height</span>);</span><br><span class="line"> avpicture_fill((AVPicture *)picture_rgb, picture_buffer_2, AV_PIX_FMT_RGB24,</span><br><span class="line"> codec_ctx-><span class="built_in">width</span>, codec_ctx-><span class="built_in">height</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (av_read_frame(format_ctx, &packet) >= <span class="number">0</span> && cnt < <span class="number">1000</span>)</span><br><span class="line"> { <span class="comment">// read ~ 1000 frames</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"1 Frame: "</span> << cnt << <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">if</span> (packet.stream_index == video_stream_index)</span><br><span class="line"> { <span class="comment">// packet is video</span></span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"2 Is Video"</span> << <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">if</span> (stream == <span class="literal">NULL</span>)</span><br><span class="line"> { <span class="comment">// create stream in file</span></span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"3 create stream"</span> << <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"> stream = avformat_new_stream(output_ctx,</span><br><span class="line"> format_ctx->streams[video_stream_index]->codec->codec);</span><br><span class="line"> avcodec_copy_context(stream->codec,</span><br><span class="line"> format_ctx->streams[video_stream_index]->codec);</span><br><span class="line"> stream->sample_aspect_ratio =</span><br><span class="line"> format_ctx->streams[video_stream_index]->codec->sample_aspect_ratio;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> check = <span class="number">0</span>;</span><br><span class="line"> packet.stream_index = stream->id;</span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"4 decoding"</span> << <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">int</span> result = avcodec_decode_video2(codec_ctx, picture, &check, &packet);</span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"Bytes decoded "</span> << result << <span class="string">" check "</span> << check</span><br><span class="line"> << <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">if</span> (cnt > <span class="number">100</span>) <span class="comment">// cnt < 0)</span></span><br><span class="line"> {</span><br><span class="line"> sws_scale(img_convert_ctx, picture->data, picture->linesize, <span class="number">0</span>,</span><br><span class="line"> codec_ctx-><span class="built_in">height</span>, picture_rgb->data, picture_rgb->linesize);</span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">stringstream</span> file_name;</span><br><span class="line"> file_name << <span class="string">"test"</span> << cnt << <span class="string">".ppm"</span>;</span><br><span class="line"> output_file.<span class="built_in">open</span>(file_name.str().c_str());</span><br><span class="line"> output_file << <span class="string">"P3 "</span> << codec_ctx-><span class="built_in">width</span> << <span class="string">" "</span> << codec_ctx-><span class="built_in">height</span></span><br><span class="line"> << <span class="string">" 255\n"</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> y = <span class="number">0</span>; y < codec_ctx-><span class="built_in">height</span>; y++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> x = <span class="number">0</span>; x < codec_ctx-><span class="built_in">width</span> * <span class="number">3</span>; x++)</span><br><span class="line"> output_file</span><br><span class="line"> << (<span class="keyword">int</span>)(picture_rgb->data[<span class="number">0</span>] + y * picture_rgb->linesize[<span class="number">0</span>])[x] << <span class="string">" "</span>;</span><br><span class="line"> }</span><br><span class="line"> output_file.<span class="built_in">close</span>();</span><br><span class="line"> }</span><br><span class="line"> cnt++;</span><br><span class="line"> }</span><br><span class="line"> av_free_packet(&packet);</span><br><span class="line"> av_init_packet(&packet);</span><br><span class="line"> }</span><br><span class="line"> av_free(picture);</span><br><span class="line"> av_free(picture_rgb);</span><br><span class="line"> av_free(picture_buffer);</span><br><span class="line"> av_free(picture_buffer_2);</span><br><span class="line"></span><br><span class="line"> av_read_pause(format_ctx);</span><br><span class="line"> avio_close(output_ctx->pb);</span><br><span class="line"> avformat_free_context(output_ctx);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (EXIT_SUCCESS);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="初始化封装上下文"><a href="#初始化封装上下文" class="headerlink" title="初始化封装上下文"></a>初始化封装上下文</h2><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Open the initial context variables that are needed</span></span><br><span class="line"> SwsContext *img_convert_ctx;</span><br><span class="line"> AVFormatContext *format_ctx = avformat_alloc_context();</span><br><span class="line"> AVCodecContext *codec_ctx = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">int</span> video_stream_index;</span><br></pre></td></tr></table></figure><p>avformat_alloc_context 初始化封装上下文, 其实这一步不太需要, 因为在后面的 avformat_open_input 方法中也会检查 format_ctx 参数, 如果没有进行初始化的话, 也会将其初始化</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></pre></td><td class="code"><pre><span class="line"><span class="function">AVFormatContext *<span class="title">avformat_alloc_context</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> FFFormatContext *<span class="keyword">const</span> si = av_mallocz(<span class="keyword">sizeof</span>(*si));</span><br><span class="line"> AVFormatContext *s;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!si)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//初始化AVStream的操作方法</span></span><br><span class="line"> s = &si->pub;</span><br><span class="line"> s->av_class = &av_format_context_class;</span><br><span class="line"> s->io_open = io_open_default;</span><br><span class="line"> s->io_close = ff_format_io_close_default;</span><br><span class="line"> s->io_close2= io_close2_default;</span><br><span class="line"></span><br><span class="line"> av_opt_set_defaults(s);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//包含音视频数据的包</span></span><br><span class="line"> si->pkt = av_packet_alloc(); </span><br><span class="line"><span class="comment">//临时数据包,只会被解析或编码程序使用, 不会被av_read_frame与ff_read_packet覆盖</span></span><br><span class="line"> si->parse_pkt = av_packet_alloc(); </span><br><span class="line"> <span class="keyword">if</span> (!si->pkt || !si->parse_pkt) {</span><br><span class="line"> avformat_free_context(s);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> si->shortest_end = AV_NOPTS_VALUE;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> s;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="打开-rtsp-流"><a href="#打开-rtsp-流" class="headerlink" title="打开 rtsp 流"></a>打开 rtsp 流</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (avformat_open_input(&format_ctx, <span class="string">"rtsp://134.169.178.187:8554/h264.3gp"</span>,</span><br><span class="line"> <span class="literal">NULL</span>, <span class="literal">NULL</span>) != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> EXIT_FAILURE;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h3 id="avformat-open-input"><a href="#avformat-open-input" class="headerlink" title="avformat_open_input"></a>avformat_open_input</h3><p>avformat_open_input 在不指定 AVInputFormat 的情况下, 会根据 filename 自动查找合适的流处理器与解封器</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><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">avformat_open_input</span><span class="params">(AVFormatContext **ps, <span class="keyword">const</span> <span class="keyword">char</span> *filename,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> AVInputFormat *fmt, AVDictionary **options)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> AVFormatContext *s = *ps;</span><br><span class="line"> FFFormatContext *si;</span><br><span class="line"> AVDictionary *tmp = <span class="literal">NULL</span>;</span><br><span class="line"> ID3v2ExtraMeta *id3v2_extra_meta = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">int</span> ret = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果事先没有初始化过上下文, 这里会初始化</span></span><br><span class="line"> <span class="keyword">if</span> (!s && !(s = avformat_alloc_context()))</span><br><span class="line"> <span class="keyword">return</span> AVERROR(ENOMEM);</span><br><span class="line"> <span class="comment">//统一转换为FFFormatContext指针</span></span><br><span class="line"> si = ffformatcontext(s); </span><br><span class="line"> <span class="keyword">if</span> (!s->av_class) {</span><br><span class="line"> av_log(<span class="literal">NULL</span>, AV_LOG_ERROR, <span class="string">"Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> AVERROR(EINVAL);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (fmt)</span><br><span class="line"> s->iformat = fmt;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (options)</span><br><span class="line"> av_dict_copy(&tmp, *options, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->pb) <span class="comment">// must be before any goto fail</span></span><br><span class="line"> s->flags |= AVFMT_FLAG_CUSTOM_IO;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((ret = av_opt_set_dict(s, &tmp)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!(s->url = av_strdup(filename ? filename : <span class="string">""</span>))) {</span><br><span class="line"> ret = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// init_input 会根据 filename 自动查找合适的解析器</span></span><br><span class="line"> <span class="keyword">if</span> ((ret = init_input(s, filename, &tmp)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> s->probe_score = ret;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {</span><br><span class="line"> s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);</span><br><span class="line"> <span class="keyword">if</span> (!s->protocol_whitelist) {</span><br><span class="line"> ret = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {</span><br><span class="line"> s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);</span><br><span class="line"> <span class="keyword">if</span> (!s->protocol_blacklist) {</span><br><span class="line"> ret = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, <span class="string">','</span>) <= <span class="number">0</span>) {</span><br><span class="line"> av_log(s, AV_LOG_ERROR, <span class="string">"Format not on whitelist \'%s\'\n"</span>, s->format_whitelist);</span><br><span class="line"> ret = AVERROR(EINVAL);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> avio_skip(s->pb, s->skip_initial_bytes);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Check filename in case an image number is expected. */</span></span><br><span class="line"> <span class="keyword">if</span> (s->iformat->flags & AVFMT_NEEDNUMBER) {</span><br><span class="line"> <span class="keyword">if</span> (!av_filename_number_test(filename)) {</span><br><span class="line"> ret = AVERROR(EINVAL);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> s->duration = s->start_time = AV_NOPTS_VALUE;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Allocate private data. */</span></span><br><span class="line"> <span class="keyword">if</span> (s->iformat->priv_data_size > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {</span><br><span class="line"> ret = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (s->iformat->priv_class) {</span><br><span class="line"> *(<span class="keyword">const</span> AVClass **) s->priv_data = s->iformat->priv_class;</span><br><span class="line"> av_opt_set_defaults(s->priv_data);</span><br><span class="line"> <span class="keyword">if</span> ((ret = av_opt_set_dict(s->priv_data, &tmp)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* e.g. AVFMT_NOFILE formats will not have an AVIOContext */</span></span><br><span class="line"> <span class="keyword">if</span> (s->pb)</span><br><span class="line"> ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->iformat->read_header)</span><br><span class="line"> <span class="keyword">if</span> ((ret = s->iformat->read_header(s)) < <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP)</span><br><span class="line"> <span class="keyword">goto</span> <span class="built_in">close</span>;</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!s->metadata) {</span><br><span class="line"> s->metadata = si->id3v2_meta;</span><br><span class="line"> si->id3v2_meta = <span class="literal">NULL</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (si->id3v2_meta) {</span><br><span class="line"> av_log(s, AV_LOG_WARNING, <span class="string">"Discarding ID3 tags because more suitable tags were found.\n"</span>);</span><br><span class="line"> av_dict_free(&si->id3v2_meta);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (id3v2_extra_meta) {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(s->iformat->name, <span class="string">"mp3"</span>) || !<span class="built_in">strcmp</span>(s->iformat->name, <span class="string">"aac"</span>) ||</span><br><span class="line"> !<span class="built_in">strcmp</span>(s->iformat->name, <span class="string">"tta"</span>) || !<span class="built_in">strcmp</span>(s->iformat->name, <span class="string">"wav"</span>)) {</span><br><span class="line"> <span class="keyword">if</span> ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> <span class="built_in">close</span>;</span><br><span class="line"> <span class="keyword">if</span> ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> <span class="built_in">close</span>;</span><br><span class="line"> <span class="keyword">if</span> ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> <span class="built_in">close</span>;</span><br><span class="line"> } <span class="keyword">else</span></span><br><span class="line"> av_log(s, AV_LOG_DEBUG, <span class="string">"demuxer does not support additional id3 data, skipping\n"</span>);</span><br><span class="line"> ff_id3v2_free_extra_meta(&id3v2_extra_meta);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((ret = avformat_queue_attached_pictures(s)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> <span class="built_in">close</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->pb && !si->data_offset)</span><br><span class="line"> si->data_offset = avio_tell(s->pb);</span><br><span class="line"></span><br><span class="line"> si->raw_packet_buffer_size = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置编解码器</span></span><br><span class="line"> update_stream_avctx(s);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (options) {</span><br><span class="line"> av_dict_free(options);</span><br><span class="line"> *options = tmp;</span><br><span class="line"> }</span><br><span class="line"> *ps = s;</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 class="built_in">close</span>:</span><br><span class="line"> <span class="keyword">if</span> (s->iformat->read_close)</span><br><span class="line"> s->iformat->read_close(s);</span><br><span class="line">fail:</span><br><span class="line"> ff_id3v2_free_extra_meta(&id3v2_extra_meta);</span><br><span class="line"> av_dict_free(&tmp);</span><br><span class="line"> <span class="keyword">if</span> (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))</span><br><span class="line"> avio_closep(&s->pb);</span><br><span class="line"> avformat_free_context(s);</span><br><span class="line"> *ps = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="init-input"><a href="#init-input" class="headerlink" title="init_input"></a>init_input</h4><p>负责查找解封器</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">init_input</span><span class="params">(AVFormatContext *s, <span class="keyword">const</span> <span class="keyword">char</span> *filename,</span></span></span><br><span class="line"><span class="function"><span class="params"> AVDictionary **options)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> ret;</span><br><span class="line"> AVProbeData pd = { filename, <span class="literal">NULL</span>, <span class="number">0</span> };</span><br><span class="line"> <span class="keyword">int</span> score = AVPROBE_SCORE_RETRY;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->pb) {</span><br><span class="line"> s->flags |= AVFMT_FLAG_CUSTOM_IO;</span><br><span class="line"> <span class="keyword">if</span> (!s->iformat)</span><br><span class="line"> <span class="comment">//如果没有制定 iformat, 但是有拿到数据的话, 则 av_probe_input_buffer2 会调用 av_probe_input_format2(pd, 1, &score) 来查找解封器.</span></span><br><span class="line"> <span class="keyword">return</span> av_probe_input_buffer2(s->pb, &s->iformat, filename,</span><br><span class="line"> s, <span class="number">0</span>, s->format_probesize);</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (s->iformat->flags & AVFMT_NOFILE)</span><br><span class="line"> av_log(s, AV_LOG_WARNING, <span class="string">"Custom AVIOContext makes no sense and "</span></span><br><span class="line"> <span class="string">"will be ignored with AVFMT_NOFILE format.\n"</span>);</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">//如果没有制定 iformat, 也没有制定的 probe 块, 则直接调用 av_probe_input_format2(pd, 0, &score) 来查找解封器.</span></span><br><span class="line"> <span class="keyword">if</span> ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||</span><br><span class="line"> (!s->iformat && (s->iformat = av_probe_input_format2(&pd, <span class="number">0</span>, &score))))</span><br><span class="line"> <span class="keyword">return</span> score;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在这里 io_open 调用的是 avformat_alloc_context 中初始化的缺省 io_open_default, </span></span><br><span class="line"> <span class="comment">// 这个方法在 options.c 文件中</span></span><br><span class="line"> <span class="keyword">if</span> ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->iformat)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> av_probe_input_buffer2(s->pb, &s->iformat, filename,</span><br><span class="line"> s, <span class="number">0</span>, s->format_probesize);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="av-probe-input-format3"><a href="#av-probe-input-format3" class="headerlink" title="av_probe_input_format3"></a>av_probe_input_format3</h5><p>av_probe_input_format2 调用 av_probe_input_format3 进行实际操作</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">const</span> AVInputFormat *<span class="title">av_probe_input_format3</span><span class="params">(<span class="keyword">const</span> AVProbeData *pd,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> is_opened, <span class="keyword">int</span> *score_ret)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> AVProbeData lpd = *pd;</span><br><span class="line"> <span class="keyword">const</span> AVInputFormat *fmt1 = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">const</span> AVInputFormat *fmt = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">int</span> score, score_max = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">void</span> *i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">static</span> <span class="keyword">uint8_t</span> zerobuffer[AVPROBE_PADDING_SIZE];</span><br><span class="line"> <span class="keyword">enum</span> nodat {</span><br><span class="line"> NO_ID3,</span><br><span class="line"> ID3_ALMOST_GREATER_PROBE,</span><br><span class="line"> ID3_GREATER_PROBE,</span><br><span class="line"> ID3_GREATER_MAX_PROBE,</span><br><span class="line"> } nodat = NO_ID3;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!lpd.buf)</span><br><span class="line"> lpd.buf = (<span class="keyword">unsigned</span> <span class="keyword">char</span> *) zerobuffer;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//如果有数据则特别处理 ID3V2 的歌词格式</span></span><br><span class="line"> <span class="keyword">if</span> (lpd.buf_size > <span class="number">10</span> && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {</span><br><span class="line"> <span class="keyword">int</span> id3len = ff_id3v2_tag_len(lpd.buf);</span><br><span class="line"> <span class="keyword">if</span> (lpd.buf_size > id3len + <span class="number">16</span>) {</span><br><span class="line"> <span class="keyword">if</span> (lpd.buf_size < <span class="number">2L</span>L*id3len + <span class="number">16</span>)</span><br><span class="line"> nodat = ID3_ALMOST_GREATER_PROBE;</span><br><span class="line"> lpd.buf += id3len;</span><br><span class="line"> lpd.buf_size -= id3len;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (id3len >= PROBE_BUF_MAX) {</span><br><span class="line"> nodat = ID3_GREATER_MAX_PROBE;</span><br><span class="line"> } <span class="keyword">else</span></span><br><span class="line"> nodat = ID3_GREATER_PROBE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ffmpeg 在 demuxer_list.c 文件中定义的 demuxer_list[] 常量, </span></span><br><span class="line"> <span class="comment">// 这个数组常量包含了本版本源代码中所有的 demuxer. </span></span><br><span class="line"> <span class="comment">// 通过 av_demuxer_iterate 方法可以对这些 demuxer 进行查找.</span></span><br><span class="line"> <span class="keyword">while</span> ((fmt1 = av_demuxer_iterate(&i))) {</span><br><span class="line"> <span class="keyword">if</span> (fmt1->flags & AVFMT_EXPERIMENTAL)</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">if</span> (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && <span class="built_in">strcmp</span>(fmt1->name, <span class="string">"image2"</span>))</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> score = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// 以 rtsp 的 demuxer 为例, 它提供了 read_probe 方法, </span></span><br><span class="line"> <span class="comment">// 并在方法中对 rtsp:// 开头的链接进行了确认</span></span><br><span class="line"> <span class="keyword">if</span> (fmt1->read_probe) {</span><br><span class="line"> score = fmt1->read_probe(&lpd);</span><br><span class="line"> <span class="keyword">if</span> (score)</span><br><span class="line"> av_log(<span class="literal">NULL</span>, AV_LOG_TRACE, <span class="string">"Probing %s score:%d size:%d\n"</span>, fmt1->name, score, lpd.buf_size);</span><br><span class="line"> <span class="keyword">if</span> (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {</span><br><span class="line"> <span class="keyword">switch</span> (nodat) {</span><br><span class="line"> <span class="keyword">case</span> NO_ID3:</span><br><span class="line"> score = FFMAX(score, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ID3_GREATER_PROBE:</span><br><span class="line"> <span class="keyword">case</span> ID3_ALMOST_GREATER_PROBE:</span><br><span class="line"> score = FFMAX(score, AVPROBE_SCORE_EXTENSION / <span class="number">2</span> - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ID3_GREATER_MAX_PROBE:</span><br><span class="line"> score = FFMAX(score, AVPROBE_SCORE_EXTENSION);</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="comment">// rtsp 的 demuxer 没有提供 extensions</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (fmt1->extensions) {</span><br><span class="line"> <span class="keyword">if</span> (av_match_ext(lpd.filename, fmt1->extensions))</span><br><span class="line"> score = AVPROBE_SCORE_EXTENSION;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// rtsp 的 demuxer 没有提供 mime_type</span></span><br><span class="line"> <span class="keyword">if</span> (av_match_name(lpd.mime_type, fmt1->mime_type)) {</span><br><span class="line"> <span class="keyword">if</span> (AVPROBE_SCORE_MIME > score) {</span><br><span class="line"> av_log(<span class="literal">NULL</span>, AV_LOG_DEBUG, <span class="string">"Probing %s score:%d increased to %d due to MIME type\n"</span>, fmt1->name, score, AVPROBE_SCORE_MIME);</span><br><span class="line"> score = AVPROBE_SCORE_MIME;</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="keyword">if</span> (score > score_max) {</span><br><span class="line"> score_max = score;</span><br><span class="line"> fmt = fmt1;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (score == score_max)</span><br><span class="line"> fmt = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (nodat == ID3_GREATER_PROBE)</span><br><span class="line"> score_max = FFMIN(AVPROBE_SCORE_EXTENSION / <span class="number">2</span> - <span class="number">1</span>, score_max);</span><br><span class="line"> *score_ret = score_max;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> fmt;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="s-gt-iformat-gt-read-header"><a href="#s-gt-iformat-gt-read-header" class="headerlink" title="s->iformat->read_header"></a>s->iformat->read_header</h4><p>读取 rtsp 头信息</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">rtsp_read_header</span><span class="params">(AVFormatContext *s)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> RTSPState *rt = s->priv_data;</span><br><span class="line"> <span class="keyword">int</span> ret;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rt->initial_timeout > <span class="number">0</span>)</span><br><span class="line"> rt->rtsp_flags |= RTSP_FLAG_LISTEN;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//如果作为服务器的话, 开始监听端口</span></span><br><span class="line"> <span class="keyword">if</span> (rt->rtsp_flags & RTSP_FLAG_LISTEN) {</span><br><span class="line"> ret = rtsp_listen(s);</span><br><span class="line"> <span class="keyword">if</span> (ret)</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 否则连接 rtsp 服务器</span></span><br><span class="line"> ret = ff_rtsp_connect(s);</span><br><span class="line"> <span class="keyword">if</span> (ret)</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"></span><br><span class="line"> rt->real_setup_cache = !s->nb_streams ? <span class="literal">NULL</span> :</span><br><span class="line"> av_calloc(s->nb_streams, <span class="number">2</span> * <span class="keyword">sizeof</span>(*rt->real_setup_cache));</span><br><span class="line"> <span class="keyword">if</span> (!rt->real_setup_cache && s->nb_streams) {</span><br><span class="line"> ret = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> rt->real_setup = rt->real_setup_cache + s->nb_streams;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果设置的 initial_pause 则连接上后不立刻开始播放 </span></span><br><span class="line"> <span class="keyword">if</span> (rt->initial_pause) {</span><br><span class="line"> <span class="comment">/* do not start immediately */</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 开始播放流</span></span><br><span class="line"> ret = rtsp_read_play(s);</span><br><span class="line"> <span class="keyword">if</span> (ret < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</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">fail:</span><br><span class="line"> rtsp_read_close(s);</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="ff-rtsp-connect"><a href="#ff-rtsp-connect" class="headerlink" title="ff_rtsp_connect"></a>ff_rtsp_connect</h5><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><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">ff_rtsp_connect</span><span class="params">(AVFormatContext *s)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> RTSPState *rt = s->priv_data;</span><br><span class="line"> <span class="keyword">char</span> proto[<span class="number">128</span>], host[<span class="number">1024</span>], path[<span class="number">1024</span>];</span><br><span class="line"> <span class="keyword">char</span> tcpname[<span class="number">1024</span>], cmd[MAX_URL_SIZE], auth[<span class="number">128</span>];</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *lower_rtsp_proto = <span class="string">"tcp"</span>;</span><br><span class="line"> <span class="keyword">int</span> port, err, tcp_fd;</span><br><span class="line"> RTSPMessageHeader reply1, *reply = &reply1;</span><br><span class="line"> <span class="keyword">int</span> lower_transport_mask = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> default_port = RTSP_DEFAULT_PORT;</span><br><span class="line"> <span class="keyword">int</span> https_tunnel = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">char</span> real_challenge[<span class="number">64</span>] = <span class="string">""</span>;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_storage</span> <span class="title">peer</span>;</span></span><br><span class="line"> <span class="keyword">socklen_t</span> peer_len = <span class="keyword">sizeof</span>(peer);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rt->rtp_port_max < rt->rtp_port_min)</span><br><span class="line"> {</span><br><span class="line"> av_log(s, AV_LOG_ERROR, <span class="string">"Invalid UDP port range, max port %d less "</span></span><br><span class="line"> <span class="string">"than min port %d\n"</span>,</span><br><span class="line"> rt->rtp_port_max,</span><br><span class="line"> rt->rtp_port_min);</span><br><span class="line"> <span class="keyword">return</span> AVERROR(EINVAL);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!ff_network_init())</span><br><span class="line"> <span class="keyword">return</span> AVERROR(EIO);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (s->max_delay < <span class="number">0</span>) <span class="comment">/* Not set by the caller */</span></span><br><span class="line"> s->max_delay = s->iformat ? DEFAULT_REORDERING_DELAY : <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//调用 ff_network_init 初始化网络</span></span><br><span class="line"> <span class="comment">// 设置缺省使用rtsp传输协议, </span></span><br><span class="line"> rt->control_transport = RTSP_MODE_PLAIN;</span><br><span class="line"> <span class="comment">// 如果设置了使用 http 或者 https 则设置使用 http tunnel 协议</span></span><br><span class="line"> <span class="keyword">if</span> (rt->lower_transport_mask & ((<span class="number">1</span> << RTSP_LOWER_TRANSPORT_HTTP) |</span><br><span class="line"> (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_HTTPS)))</span><br><span class="line"> {</span><br><span class="line"> https_tunnel = !!(rt->lower_transport_mask & (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_HTTPS));</span><br><span class="line"> rt->lower_transport_mask = <span class="number">1</span> << RTSP_LOWER_TRANSPORT_TCP;</span><br><span class="line"> rt->control_transport = RTSP_MODE_TUNNEL;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* Only pass through valid flags from here */</span></span><br><span class="line"> rt->lower_transport_mask &= (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_NB) - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">redirect:</span><br><span class="line"> <span class="built_in">memset</span>(&reply1, <span class="number">0</span>, <span class="keyword">sizeof</span>(reply1));</span><br><span class="line"> <span class="comment">/* extract hostname and port */</span></span><br><span class="line"> av_url_split(proto, <span class="keyword">sizeof</span>(proto), auth, <span class="keyword">sizeof</span>(auth),</span><br><span class="line"> host, <span class="keyword">sizeof</span>(host), &port, path, <span class="keyword">sizeof</span>(path), s->url);</span><br><span class="line"><span class="comment">// 在 rtsps 协议情况下会使用322端口, 否则使用缺省的554端口</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(proto, <span class="string">"rtsps"</span>))</span><br><span class="line"> {</span><br><span class="line"> lower_rtsp_proto = <span class="string">"tls"</span>;</span><br><span class="line"> default_port = RTSPS_DEFAULT_PORT;</span><br><span class="line"> rt->lower_transport_mask = <span class="number">1</span> << RTSP_LOWER_TRANSPORT_TCP;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(proto, <span class="string">"satip"</span>))</span><br><span class="line"> {</span><br><span class="line"> av_strlcpy(proto, <span class="string">"rtsp"</span>, <span class="keyword">sizeof</span>(proto));</span><br><span class="line"> rt->server_type = RTSP_SERVER_SATIP;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (*auth)</span><br><span class="line"> {</span><br><span class="line"> av_strlcpy(rt->auth, auth, <span class="keyword">sizeof</span>(rt->auth));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (port < <span class="number">0</span>)</span><br><span class="line"> port = default_port;</span><br><span class="line"></span><br><span class="line"> lower_transport_mask = rt->lower_transport_mask;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!lower_transport_mask)</span><br><span class="line"> lower_transport_mask = (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_NB) - <span class="number">1</span>;</span><br><span class="line"><span class="comment">// 在 RTSP_MODE_TUNNEL 情况下是不支持上传流数据的</span></span><br><span class="line"> <span class="keyword">if</span> (s->oformat)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* Only UDP or TCP - UDP multicast isn't supported. */</span></span><br><span class="line"> lower_transport_mask &= (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_UDP) |</span><br><span class="line"> (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_TCP);</span><br><span class="line"> <span class="keyword">if</span> (!lower_transport_mask || rt->control_transport == RTSP_MODE_TUNNEL)</span><br><span class="line"> {</span><br><span class="line"> av_log(s, AV_LOG_ERROR, <span class="string">"Unsupported lower transport method, "</span></span><br><span class="line"> <span class="string">"only UDP and TCP are supported for output.\n"</span>);</span><br><span class="line"> err = AVERROR(EINVAL);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Construct the URI used in request; this is similar to s->url,</span></span><br><span class="line"><span class="comment"> * but with authentication credentials removed and RTSP specific options</span></span><br><span class="line"><span class="comment"> * stripped out. */</span></span><br><span class="line"> <span class="comment">// 生成 rtsp 控制地址</span></span><br><span class="line"> ff_url_join(rt->control_uri, <span class="keyword">sizeof</span>(rt->control_uri), proto, <span class="literal">NULL</span>,</span><br><span class="line"> host, port, <span class="string">"%s"</span>, path);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rt->control_transport == RTSP_MODE_TUNNEL)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* set up initial handshake for tunneling */</span></span><br><span class="line"> <span class="keyword">char</span> httpname[<span class="number">1024</span>];</span><br><span class="line"> <span class="keyword">char</span> sessioncookie[<span class="number">17</span>];</span><br><span class="line"> <span class="keyword">char</span> headers[<span class="number">1024</span>];</span><br><span class="line"> AVDictionary *options = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> av_dict_set_int(&options, <span class="string">"timeout"</span>, rt->stimeout, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> ff_url_join(httpname, <span class="keyword">sizeof</span>(httpname), https_tunnel ? <span class="string">"https"</span> : <span class="string">"http"</span>, auth, host, port, <span class="string">"%s"</span>, path);</span><br><span class="line"> <span class="built_in">snprintf</span>(sessioncookie, <span class="keyword">sizeof</span>(sessioncookie), <span class="string">"%08x%08x"</span>,</span><br><span class="line"> av_get_random_seed(), av_get_random_seed());</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* GET requests */</span></span><br><span class="line"> <span class="keyword">if</span> (ffurl_alloc(&rt->rtsp_hd, httpname, AVIO_FLAG_READ,</span><br><span class="line"> &s->interrupt_callback) < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> err = AVERROR(EIO);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* generate GET headers */</span></span><br><span class="line"> <span class="built_in">snprintf</span>(headers, <span class="keyword">sizeof</span>(headers),</span><br><span class="line"> <span class="string">"x-sessioncookie: %s\r\n"</span></span><br><span class="line"> <span class="string">"Accept: application/x-rtsp-tunnelled\r\n"</span></span><br><span class="line"> <span class="string">"Pragma: no-cache\r\n"</span></span><br><span class="line"> <span class="string">"Cache-Control: no-cache\r\n"</span>,</span><br><span class="line"> sessioncookie);</span><br><span class="line"> av_opt_set(rt->rtsp_hd->priv_data, <span class="string">"headers"</span>, headers, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!rt->rtsp_hd->protocol_whitelist && s->protocol_whitelist)</span><br><span class="line"> {</span><br><span class="line"> rt->rtsp_hd->protocol_whitelist = av_strdup(s->protocol_whitelist);</span><br><span class="line"> <span class="keyword">if</span> (!rt->rtsp_hd->protocol_whitelist)</span><br><span class="line"> {</span><br><span class="line"> err = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* complete the connection */</span></span><br><span class="line"> <span class="keyword">if</span> (ffurl_connect(rt->rtsp_hd, &options))</span><br><span class="line"> {</span><br><span class="line"> av_dict_free(&options);</span><br><span class="line"> err = AVERROR(EIO);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* POST requests */</span></span><br><span class="line"> <span class="keyword">if</span> (ffurl_alloc(&rt->rtsp_hd_out, httpname, AVIO_FLAG_WRITE,</span><br><span class="line"> &s->interrupt_callback) < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> err = AVERROR(EIO);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* generate POST headers */</span></span><br><span class="line"> <span class="built_in">snprintf</span>(headers, <span class="keyword">sizeof</span>(headers),</span><br><span class="line"> <span class="string">"x-sessioncookie: %s\r\n"</span></span><br><span class="line"> <span class="string">"Content-Type: application/x-rtsp-tunnelled\r\n"</span></span><br><span class="line"> <span class="string">"Pragma: no-cache\r\n"</span></span><br><span class="line"> <span class="string">"Cache-Control: no-cache\r\n"</span></span><br><span class="line"> <span class="string">"Content-Length: 32767\r\n"</span></span><br><span class="line"> <span class="string">"Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n"</span>,</span><br><span class="line"> sessioncookie);</span><br><span class="line"> av_opt_set(rt->rtsp_hd_out->priv_data, <span class="string">"headers"</span>, headers, <span class="number">0</span>);</span><br><span class="line"> av_opt_set(rt->rtsp_hd_out->priv_data, <span class="string">"chunked_post"</span>, <span class="string">"0"</span>, <span class="number">0</span>);</span><br><span class="line"> av_opt_set(rt->rtsp_hd_out->priv_data, <span class="string">"send_expect_100"</span>, <span class="string">"0"</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Initialize the authentication state for the POST session. The HTTP</span></span><br><span class="line"><span class="comment"> * protocol implementation doesn't properly handle multi-pass</span></span><br><span class="line"><span class="comment"> * authentication for POST requests, since it would require one of</span></span><br><span class="line"><span class="comment"> * the following:</span></span><br><span class="line"><span class="comment"> * - implementing Expect: 100-continue, which many HTTP servers</span></span><br><span class="line"><span class="comment"> * don't support anyway, even less the RTSP servers that do HTTP</span></span><br><span class="line"><span class="comment"> * tunneling</span></span><br><span class="line"><span class="comment"> * - sending the whole POST data until getting a 401 reply specifying</span></span><br><span class="line"><span class="comment"> * what authentication method to use, then resending all that data</span></span><br><span class="line"><span class="comment"> * - waiting for potential 401 replies directly after sending the</span></span><br><span class="line"><span class="comment"> * POST header (waiting for some unspecified time)</span></span><br><span class="line"><span class="comment"> * Therefore, we copy the full auth state, which works for both basic</span></span><br><span class="line"><span class="comment"> * and digest. (For digest, we would have to synchronize the nonce</span></span><br><span class="line"><span class="comment"> * count variable between the two sessions, if we'd do more requests</span></span><br><span class="line"><span class="comment"> * with the original session, though.)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> ff_http_init_auth_state(rt->rtsp_hd_out, rt->rtsp_hd);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* complete the connection */</span></span><br><span class="line"> <span class="keyword">if</span> (ffurl_connect(rt->rtsp_hd_out, &options))</span><br><span class="line"> {</span><br><span class="line"> av_dict_free(&options);</span><br><span class="line"> err = AVERROR(EIO);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> av_dict_free(&options);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="comment">//使用缺省的 rtsp 协议</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> ret;</span><br><span class="line"> <span class="comment">/* open the tcp connection */</span></span><br><span class="line"> ff_url_join(tcpname, <span class="keyword">sizeof</span>(tcpname), lower_rtsp_proto, <span class="literal">NULL</span>,</span><br><span class="line"> host, port,</span><br><span class="line"> <span class="string">"?timeout=%"</span> PRId64, rt->stimeout);</span><br><span class="line"> <span class="comment">// 打开 rtsp 流地址, 在 ffurl_open_whitelist 中会</span></span><br><span class="line"> <span class="comment">// 通过 ffurl_alloc 函数生成 rtsp_hd 这个 URLContext 对象</span></span><br><span class="line"> <span class="keyword">if</span> ((ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,</span><br><span class="line"> &s->interrupt_callback, <span class="literal">NULL</span>, s->protocol_whitelist, s->protocol_blacklist, <span class="literal">NULL</span>)) < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> err = ret;</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> rt->rtsp_hd_out = rt->rtsp_hd;</span><br><span class="line"> }</span><br><span class="line"> rt->seq = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> tcp_fd = ffurl_get_file_handle(rt->rtsp_hd);</span><br><span class="line"> <span class="keyword">if</span> (tcp_fd < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> err = tcp_fd;</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!getpeername(tcp_fd, (struct sockaddr *)&peer, &peer_len))</span><br><span class="line"> {</span><br><span class="line"> getnameinfo((struct sockaddr *)&peer, peer_len, host, <span class="keyword">sizeof</span>(host),</span><br><span class="line"> <span class="literal">NULL</span>, <span class="number">0</span>, NI_NUMERICHOST);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* request options supported by the server; this also detects server</span></span><br><span class="line"><span class="comment"> * type */</span></span><br><span class="line"> <span class="keyword">if</span> (rt->server_type != RTSP_SERVER_SATIP)</span><br><span class="line"> rt->server_type = RTSP_SERVER_RTP;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 向服务器发出 OPTIONS 命令, 直到返回错误或者正确的信息, </span></span><br><span class="line"> <span class="comment">// OPTIONS 命令会告诉客户端, 服务端的能力, 并且也表明服务端正确响应了客户端,</span></span><br><span class="line"> <span class="comment">// 可以开始进一步的通讯了</span></span><br><span class="line"> <span class="keyword">for</span> (;;)</span><br><span class="line"> {</span><br><span class="line"> cmd[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (rt->server_type == RTSP_SERVER_REAL)</span><br><span class="line"> av_strlcat(cmd,</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The following entries are required for proper</span></span><br><span class="line"><span class="comment"> * streaming from a Realmedia server. They are</span></span><br><span class="line"><span class="comment"> * interdependent in some way although we currently</span></span><br><span class="line"><span class="comment"> * don't quite understand how. Values were copied</span></span><br><span class="line"><span class="comment"> * from mplayer SVN r23589.</span></span><br><span class="line"><span class="comment"> * ClientChallenge is a 16-byte ID in hex</span></span><br><span class="line"><span class="comment"> * CompanyID is a 16-byte ID in base64</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="string">"ClientChallenge: 9e26d33f2984236010ef6253fb1887f7\r\n"</span></span><br><span class="line"> <span class="string">"PlayerStarttime: [28/03/2003:22:50:23 00:00]\r\n"</span></span><br><span class="line"> <span class="string">"CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n"</span></span><br><span class="line"> <span class="string">"GUID: 00000000-0000-0000-0000-000000000000\r\n"</span>,</span><br><span class="line"> <span class="keyword">sizeof</span>(cmd));</span><br><span class="line"> ff_rtsp_send_cmd(s, <span class="string">"OPTIONS"</span>, rt->control_uri, cmd, reply, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">if</span> (reply->status_code != RTSP_STATUS_OK)</span><br><span class="line"> {</span><br><span class="line"> err = ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* detect server type if not standard-compliant RTP */</span></span><br><span class="line"> <span class="keyword">if</span> (rt->server_type != RTSP_SERVER_REAL && reply->real_challenge[<span class="number">0</span>])</span><br><span class="line"> {</span><br><span class="line"> rt->server_type = RTSP_SERVER_REAL;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (!av_strncasecmp(reply->server, <span class="string">"WMServer/"</span>, <span class="number">9</span>))</span><br><span class="line"> {</span><br><span class="line"> rt->server_type = RTSP_SERVER_WMS;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (rt->server_type == RTSP_SERVER_REAL)</span><br><span class="line"> <span class="built_in">strcpy</span>(real_challenge, reply->real_challenge);</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">if</span> CONFIG_RTSP_DEMUXER</span></span><br><span class="line"> <span class="keyword">if</span> (s->iformat)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (rt->server_type == RTSP_SERVER_SATIP)</span><br><span class="line"> err = init_satip_stream(s);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="comment">// 设置解析流</span></span><br><span class="line"> err = ff_rtsp_setup_input_streams(s, reply);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line">#endif</span><br><span class="line"> <span class="keyword">if</span> (CONFIG_RTSP_MUXER)</span><br><span class="line"> err = ff_rtsp_setup_output_streams(s, host);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> av_assert0(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span> (err)</span><br><span class="line"> <span class="keyword">goto</span> fail;</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> lower_transport = ff_log2_tab[lower_transport_mask &</span><br><span class="line"> ~(lower_transport_mask - <span class="number">1</span>)];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((lower_transport_mask & (<span class="number">1</span> << RTSP_LOWER_TRANSPORT_TCP)) && (rt->rtsp_flags & RTSP_FLAG_PREFER_TCP))</span><br><span class="line"> lower_transport = RTSP_LOWER_TRANSPORT_TCP;</span><br><span class="line"></span><br><span class="line"> err = ff_rtsp_make_setup_request(s, host, port, lower_transport,</span><br><span class="line"> rt->server_type == RTSP_SERVER_REAL ? real_challenge : <span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">if</span> (err < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> lower_transport_mask &= ~(<span class="number">1</span> << lower_transport);</span><br><span class="line"> <span class="keyword">if</span> (lower_transport_mask == <span class="number">0</span> && err == <span class="number">1</span>)</span><br><span class="line"> {</span><br><span class="line"> err = AVERROR(EPROTONOSUPPORT);</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">while</span> (err);</span><br><span class="line"></span><br><span class="line"> rt->lower_transport_mask = lower_transport_mask;</span><br><span class="line"> av_strlcpy(rt->real_challenge, real_challenge, <span class="keyword">sizeof</span>(rt->real_challenge));</span><br><span class="line"> rt->state = RTSP_STATE_IDLE;</span><br><span class="line"> rt->seek_timestamp = <span class="number">0</span>; <span class="comment">/* default is to start stream at position zero */</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">fail:</span><br><span class="line"> ff_rtsp_close_streams(s);</span><br><span class="line"> ff_rtsp_close_connections(s);</span><br><span class="line"> <span class="keyword">if</span> (reply->status_code >= <span class="number">300</span> && reply->status_code < <span class="number">400</span> && s->iformat)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">char</span> *new_url = av_strdup(reply->location);</span><br><span class="line"> <span class="keyword">if</span> (!new_url)</span><br><span class="line"> {</span><br><span class="line"> err = AVERROR(ENOMEM);</span><br><span class="line"> <span class="keyword">goto</span> fail2;</span><br><span class="line"> }</span><br><span class="line"> ff_format_set_url(s, new_url);</span><br><span class="line"> rt->session_id[<span class="number">0</span>] = <span class="string">'\0'</span>;</span><br><span class="line"> av_log(s, AV_LOG_INFO, <span class="string">"Status %d: Redirecting to %s\n"</span>,</span><br><span class="line"> reply->status_code,</span><br><span class="line"> s->url);</span><br><span class="line"> <span class="keyword">goto</span> redirect;</span><br><span class="line"> }</span><br><span class="line">fail2:</span><br><span class="line"> ff_network_close();</span><br><span class="line"> <span class="keyword">return</span> err;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="ffurl-open-whitelist"><a href="#ffurl-open-whitelist" class="headerlink" title="ffurl_open_whitelist"></a>ffurl_open_whitelist</h5><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">ffurl_open_whitelist</span><span class="params">(URLContext **puc, <span class="keyword">const</span> <span class="keyword">char</span> *filename, <span class="keyword">int</span> flags,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> AVIOInterruptCB *int_cb, AVDictionary **options,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> <span class="keyword">char</span> *whitelist, <span class="keyword">const</span> <span class="keyword">char</span>* blacklist,</span></span></span><br><span class="line"><span class="function"><span class="params"> URLContext *parent)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> AVDictionary *tmp_opts = <span class="literal">NULL</span>;</span><br><span class="line"> AVDictionaryEntry *e;</span><br><span class="line"> <span class="comment">// 通过 avio.c 文件中的 url_find_protocol 函数, 使用 </span></span><br><span class="line"> <span class="comment">// ffurl_get_protocols 在 protocol_list.c 文件定义的常量 url_protocols 中找到 </span></span><br><span class="line"> <span class="comment">// ff_rtp_protocol 这个 rtp 传输协议解析器</span></span><br><span class="line"> <span class="comment">// 然后把传输协议放入 puc->prot</span></span><br><span class="line"> <span class="keyword">int</span> ret = ffurl_alloc(puc, filename, flags, int_cb);</span><br><span class="line"> <span class="keyword">if</span> (ret < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> <span class="keyword">if</span> (parent) {</span><br><span class="line"> ret = av_opt_copy(*puc, parent);</span><br><span class="line"> <span class="keyword">if</span> (ret < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (options &&</span><br><span class="line"> (ret = av_opt_set_dict(*puc, options)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"> <span class="keyword">if</span> (options && (*puc)->prot->priv_data_class &&</span><br><span class="line"> (ret = av_opt_set_dict((*puc)->priv_data, options)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!options)</span><br><span class="line"> options = &tmp_opts;</span><br><span class="line"></span><br><span class="line"> av_assert0(!whitelist ||</span><br><span class="line"> !(e=av_dict_get(*options, <span class="string">"protocol_whitelist"</span>, <span class="literal">NULL</span>, <span class="number">0</span>)) ||</span><br><span class="line"> !<span class="built_in">strcmp</span>(whitelist, e->value));</span><br><span class="line"> av_assert0(!blacklist ||</span><br><span class="line"> !(e=av_dict_get(*options, <span class="string">"protocol_blacklist"</span>, <span class="literal">NULL</span>, <span class="number">0</span>)) ||</span><br><span class="line"> !<span class="built_in">strcmp</span>(blacklist, e->value));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((ret = av_dict_set(options, <span class="string">"protocol_whitelist"</span>, whitelist, <span class="number">0</span>)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((ret = av_dict_set(options, <span class="string">"protocol_blacklist"</span>, blacklist, <span class="number">0</span>)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((ret = av_opt_set_dict(*puc, options)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">goto</span> fail;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用得到的 rtp 传输协议连接 rtsp 服务</span></span><br><span class="line"> ret = ffurl_connect(*puc, options);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!ret)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">fail:</span><br><span class="line"> ffurl_closep(puc);</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="url-protocols"><a href="#url-protocols" class="headerlink" title="url_protocols"></a>url_protocols</h5><p>protocol_list.c 文件定义的常量 url_protocols 中找到 ff_rtp_protocol 这个 rtp 传输协议解析器.</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> URLProtocol ff_rtp_protocol = {</span><br><span class="line"> .name = <span class="string">"rtp"</span>,</span><br><span class="line"> .url_open = rtp_open,</span><br><span class="line"> .url_read = rtp_read,</span><br><span class="line"> .url_write = rtp_write,</span><br><span class="line"> .url_close = rtp_close,</span><br><span class="line"> .url_get_file_handle = rtp_get_file_handle,</span><br><span class="line"> .url_get_multi_file_handle = rtp_get_multi_file_handle,</span><br><span class="line"> .priv_data_size = <span class="keyword">sizeof</span>(RTPContext),</span><br><span class="line"> .flags = URL_PROTOCOL_FLAG_NETWORK,</span><br><span class="line"> .priv_data_class = &rtp_class,</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h5 id="ffurl-connect"><a href="#ffurl-connect" class="headerlink" title="ffurl_connect"></a>ffurl_connect</h5><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">ffurl_connect</span><span class="params">(URLContext *uc, AVDictionary **options)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> err;</span><br><span class="line"> AVDictionary *tmp_opts = <span class="literal">NULL</span>;</span><br><span class="line"> AVDictionaryEntry *e;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!options)</span><br><span class="line"> options = &tmp_opts;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Check that URLContext was initialized correctly and lists are matching if set</span></span><br><span class="line"> av_assert0(!(e=av_dict_get(*options, <span class="string">"protocol_whitelist"</span>, <span class="literal">NULL</span>, <span class="number">0</span>)) ||</span><br><span class="line"> (uc->protocol_whitelist && !<span class="built_in">strcmp</span>(uc->protocol_whitelist, e->value)));</span><br><span class="line"> av_assert0(!(e=av_dict_get(*options, <span class="string">"protocol_blacklist"</span>, <span class="literal">NULL</span>, <span class="number">0</span>)) ||</span><br><span class="line"> (uc->protocol_blacklist && !<span class="built_in">strcmp</span>(uc->protocol_blacklist, e->value)));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, <span class="string">','</span>) <= <span class="number">0</span>) {</span><br><span class="line"> av_log(uc, AV_LOG_ERROR, <span class="string">"Protocol '%s' not on whitelist '%s'!\n"</span>, uc->prot->name, uc->protocol_whitelist);</span><br><span class="line"> <span class="keyword">return</span> AVERROR(EINVAL);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, <span class="string">','</span>) > <span class="number">0</span>) {</span><br><span class="line"> av_log(uc, AV_LOG_ERROR, <span class="string">"Protocol '%s' on blacklist '%s'!\n"</span>, uc->prot->name, uc->protocol_blacklist);</span><br><span class="line"> <span class="keyword">return</span> AVERROR(EINVAL);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!uc->protocol_whitelist && uc->prot->default_whitelist) {</span><br><span class="line"> av_log(uc, AV_LOG_DEBUG, <span class="string">"Setting default whitelist '%s'\n"</span>, uc->prot->default_whitelist);</span><br><span class="line"> uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist);</span><br><span class="line"> <span class="keyword">if</span> (!uc->protocol_whitelist) {</span><br><span class="line"> <span class="keyword">return</span> AVERROR(ENOMEM);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (!uc->protocol_whitelist)</span><br><span class="line"> av_log(uc, AV_LOG_DEBUG, <span class="string">"No default whitelist set\n"</span>); <span class="comment">// This should be an error once all declare a default whitelist</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((err = av_dict_set(options, <span class="string">"protocol_whitelist"</span>, uc->protocol_whitelist, <span class="number">0</span>)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> err;</span><br><span class="line"> <span class="keyword">if</span> ((err = av_dict_set(options, <span class="string">"protocol_blacklist"</span>, uc->protocol_blacklist, <span class="number">0</span>)) < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> err;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用 rtp 协议中的 rtp_open 方法连接s</span></span><br><span class="line"> err =</span><br><span class="line"> uc->prot->url_open2 ? uc->prot->url_open2(uc,</span><br><span class="line"> uc->filename,</span><br><span class="line"> uc->flags,</span><br><span class="line"> options) :</span><br><span class="line"> uc->prot->url_open(uc, uc->filename, uc->flags);</span><br><span class="line"></span><br><span class="line"> av_dict_set(options, <span class="string">"protocol_whitelist"</span>, <span class="literal">NULL</span>, <span class="number">0</span>);</span><br><span class="line"> av_dict_set(options, <span class="string">"protocol_blacklist"</span>, <span class="literal">NULL</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (err)</span><br><span class="line"> <span class="keyword">return</span> err;</span><br><span class="line"> uc->is_connected = <span class="number">1</span>;</span><br><span class="line"> <span class="comment">/* We must be careful here as ffurl_seek() could be slow,</span></span><br><span class="line"><span class="comment"> * for example for http */</span></span><br><span class="line"> <span class="keyword">if</span> ((uc->flags & AVIO_FLAG_WRITE) || !<span class="built_in">strcmp</span>(uc->prot->name, <span class="string">"file"</span>))</span><br><span class="line"> <span class="keyword">if</span> (!uc->is_streamed && ffurl_seek(uc, <span class="number">0</span>, SEEK_SET) < <span class="number">0</span>)</span><br><span class="line"> uc->is_streamed = <span class="number">1</span>;</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></pre></td></tr></table></figure><h5 id="rtp-open"><a href="#rtp-open" class="headerlink" title="rtp_open"></a>rtp_open</h5><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">rtp_open</span><span class="params">(URLContext *h, <span class="keyword">const</span> <span class="keyword">char</span> *uri, <span class="keyword">int</span> flags)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// rtp的实际连接是个udp连接, 在这个函数中根据rtp连接字符串重新构建udp连接字符串</span></span><br><span class="line"> <span class="comment">// 再通过 ffurl_open_whitelist 函数去打开 udp 连接</span></span><br><span class="line"> <span class="comment">// 这里打开的 upd 连接有两个, 一个是 rtp 的数据传输连接, 一个是 rtcp 的协议控制连接</span></span><br><span class="line"> <span class="comment">// fec 前向错误矫正, 如果有这的话, 还要多一个连接</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="ff-rtsp-setup-input-stream"><a href="#ff-rtsp-setup-input-stream" class="headerlink" title="ff_rtsp_setup_input_stream"></a>ff_rtsp_setup_input_stream</h5><p>发送 DESCRIBE 命令, 得到媒体流的格式数据, 通过 ff_sdp_parse 解析返回的结果后对解封装器进行设置, 其中</p><ol><li>如果不是MP2T的封装格式, 通过 ff_rtp_handler_find_by_id 从 rtpdesc.c 文件中定义的 rtp_dynamic_protocol_handler_list 中找到解码器</li><li>如果是私有协议类型, ff_rtp_get_codec_info 函数得到缺省支持的媒体类型, 然后再从 ff_rtp_handler_find_by_id 中得到对应的解码器</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">ff_rtsp_setup_input_streams</span><span class="params">(AVFormatContext *s, RTSPMessageHeader *reply)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> RTSPState *rt = s->priv_data;</span><br><span class="line"> <span class="keyword">char</span> cmd[MAX_URL_SIZE];</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">char</span> *content = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">int</span> ret;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* describe the stream */</span></span><br><span class="line"> <span class="built_in">snprintf</span>(cmd, <span class="keyword">sizeof</span>(cmd),</span><br><span class="line"> <span class="string">"Accept: application/sdp\r\n"</span>);</span><br><span class="line"> <span class="keyword">if</span> (rt->server_type == RTSP_SERVER_REAL) {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The Require: attribute is needed for proper streaming from</span></span><br><span class="line"><span class="comment"> * Realmedia servers.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> av_strlcat(cmd,</span><br><span class="line"> <span class="string">"Require: com.real.retain-entity-for-setup\r\n"</span>,</span><br><span class="line"> <span class="keyword">sizeof</span>(cmd));</span><br><span class="line"> }</span><br><span class="line"> ff_rtsp_send_cmd(s, <span class="string">"DESCRIBE"</span>, rt->control_uri, cmd, reply, &content);</span><br><span class="line"> <span class="keyword">if</span> (reply->status_code != RTSP_STATUS_OK) {</span><br><span class="line"> av_freep(&content);</span><br><span class="line"> <span class="keyword">return</span> ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!content)</span><br><span class="line"> <span class="keyword">return</span> AVERROR_INVALIDDATA;</span><br><span class="line"></span><br><span class="line"> av_log(s, AV_LOG_VERBOSE, <span class="string">"SDP:\n%s\n"</span>, content);</span><br><span class="line"> <span class="comment">/* now we got the SDP description, we parse it */</span></span><br><span class="line"> ret = ff_sdp_parse(s, (<span class="keyword">const</span> <span class="keyword">char</span> *)content);</span><br><span class="line"> av_freep(&content);</span><br><span class="line"> <span class="keyword">if</span> (ret < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> ret;</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><h5 id="ff-rtsp-make-setup-request"><a href="#ff-rtsp-make-setup-request" class="headerlink" title="ff_rtsp_make_setup_request"></a>ff_rtsp_make_setup_request</h5><p>发送 SETUP 命令给 rtsp 服务器</p><h5 id="rtsp-read-play"><a href="#rtsp-read-play" class="headerlink" title="rtsp_read_play"></a>rtsp_read_play</h5><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">rtsp_read_play</span><span class="params">(AVFormatContext *s)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> RTSPState *rt = s->priv_data;</span><br><span class="line"> RTSPMessageHeader reply1, *reply = &reply1;</span><br><span class="line"> <span class="keyword">int</span> i;</span><br><span class="line"> <span class="keyword">char</span> cmd[MAX_URL_SIZE];</span><br><span class="line"></span><br><span class="line"> av_log(s, AV_LOG_DEBUG, <span class="string">"hello state=%d\n"</span>, rt->state);</span><br><span class="line"> rt->nb_byes = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP) {</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < rt->nb_rtsp_streams; i++) {</span><br><span class="line"> RTSPStream *rtsp_st = rt->rtsp_streams[i];</span><br><span class="line"> <span class="comment">/* Try to initialize the connection state in a</span></span><br><span class="line"><span class="comment"> * potential NAT router by sending dummy packets.</span></span><br><span class="line"><span class="comment"> * RTP/RTCP dummy packets are used for RDT, too.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (rtsp_st->rtp_handle &&</span><br><span class="line"> !(rt->server_type == RTSP_SERVER_WMS && i > <span class="number">1</span>))</span><br><span class="line"> ff_rtp_send_punch_packets(rtsp_st->rtp_handle);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) {</span><br><span class="line"> <span class="keyword">if</span> (rt->transport == RTSP_TRANSPORT_RTP) {</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < rt->nb_rtsp_streams; i++) {</span><br><span class="line"> RTSPStream *rtsp_st = rt->rtsp_streams[i];</span><br><span class="line"> RTPDemuxContext *rtpctx = rtsp_st->transport_priv;</span><br><span class="line"> <span class="keyword">if</span> (!rtpctx)</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> ff_rtp_reset_packet_queue(rtpctx);</span><br><span class="line"> rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE;</span><br><span class="line"> rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE;</span><br><span class="line"> rtpctx->base_timestamp = <span class="number">0</span>;</span><br><span class="line"> rtpctx->timestamp = <span class="number">0</span>;</span><br><span class="line"> rtpctx->unwrapped_timestamp = <span class="number">0</span>;</span><br><span class="line"> rtpctx->rtcp_ts_offset = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (rt->state == RTSP_STATE_PAUSED) {</span><br><span class="line"> cmd[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">snprintf</span>(cmd, <span class="keyword">sizeof</span>(cmd),</span><br><span class="line"> <span class="string">"Range: npt=%"</span>PRId64<span class="string">".%03"</span>PRId64<span class="string">"-\r\n"</span>,</span><br><span class="line"> rt->seek_timestamp / AV_TIME_BASE,</span><br><span class="line"> rt->seek_timestamp / (AV_TIME_BASE / <span class="number">1000</span>) % <span class="number">1000</span>);</span><br><span class="line"> }</span><br><span class="line"> ff_rtsp_send_cmd(s, <span class="string">"PLAY"</span>, rt->control_uri, cmd, reply, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">if</span> (reply->status_code != RTSP_STATUS_OK) {</span><br><span class="line"> <span class="keyword">return</span> ff_rtsp_averror(reply->status_code, <span class="number">-1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (rt->transport == RTSP_TRANSPORT_RTP &&</span><br><span class="line"> reply->range_start != AV_NOPTS_VALUE) {</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < rt->nb_rtsp_streams; i++) {</span><br><span class="line"> RTSPStream *rtsp_st = rt->rtsp_streams[i];</span><br><span class="line"> RTPDemuxContext *rtpctx = rtsp_st->transport_priv;</span><br><span class="line"> AVStream *st = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">if</span> (!rtpctx || rtsp_st->stream_index < <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line"> st = s->streams[rtsp_st->stream_index];</span><br><span class="line"> rtpctx->range_start_offset =</span><br><span class="line"> av_rescale_q(reply->range_start, AV_TIME_BASE_Q,</span><br><span class="line"> st->time_base);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> rt->state = RTSP_STATE_STREAMING;</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><h2 id="查询流信息"><a href="#查询流信息" class="headerlink" title="查询流信息"></a>查询流信息</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (avformat_find_stream_info(format_ctx, <span class="literal">NULL</span>) < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> EXIT_FAILURE;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h3 id="avformat-find-stream-info"><a href="#avformat-find-stream-info" class="headerlink" title="avformat_find_stream_info"></a>avformat_find_stream_info</h3><p> </p>]]></content>
<summary type="html"><h2 id="分析的源码"><a href="#分析的源码" class="headerlink" title="分析的源码"></a>分析的源码</h2><figure class="highlight c++"><table><tr><td class="gutter"><</summary>
<category term="编程 ffmpeg rtsp" scheme="http://horsefaced.github.io/tags/%E7%BC%96%E7%A8%8B-ffmpeg-rtsp/"/>
</entry>
<entry>
<title>装备减重计划</title>
<link href="http://horsefaced.github.io/2020/10/20/%E8%A3%85%E5%A4%87%E5%87%8F%E9%87%8D%E8%AE%A1%E5%88%92/"/>
<id>http://horsefaced.github.io/2020/10/20/%E8%A3%85%E5%A4%87%E5%87%8F%E9%87%8D%E8%AE%A1%E5%88%92/</id>
<published>2020-10-20T10:47:40.000Z</published>
<updated>2020-10-20T11:02:49.439Z</updated>
<content type="html"><![CDATA[<h2 id="背包"><a href="#背包" class="headerlink" title="背包"></a>背包</h2><h3 id="sixmoondesign的swift-X和minimalism"><a href="#sixmoondesign的swift-X和minimalism" class="headerlink" title="sixmoondesign的swift X和minimalism"></a>sixmoondesign的swift X和minimalism</h3><ul><li><p>优势</p></li><li><ul><li>肩带仿照越野跑背包肩带,方便饮用水和路粮</li></ul></li><li><p>劣势</p></li><li><ul><li>重量对比hmg2400没有明显的降低</li><li>侧面网兜的结实性有待验证</li></ul></li></ul><h3 id="waymark-lite"><a href="#waymark-lite" class="headerlink" title="waymark lite"></a>waymark lite</h3><ul><li>优势<ul><li>可定制大的侧袋</li><li>前兜网可定制为网兜</li></ul></li><li>劣势<ul><li>无背负,需要验证整体装备的重量和转包能力</li></ul></li></ul><h3 id="HMG-2400"><a href="#HMG-2400" class="headerlink" title="HMG 2400"></a>HMG 2400</h3><ul><li>优势<ul><li>我有</li></ul></li><li>劣势<ul><li>可能容量不够</li></ul></li></ul><h2 id="帐篷"><a href="#帐篷" class="headerlink" title="帐篷"></a>帐篷</h2><h3 id="tarptent-notch-li"><a href="#tarptent-notch-li" class="headerlink" title="tarptent notch li"></a>tarptent notch li</h3><ul><li>优势<ul><li>重量在考虑范围内</li><li>成型设计,只需要六根地丁</li></ul></li><li>劣势<ul><li>太贵</li><li>0.5oz外皮可能会太脆弱</li></ul></li></ul><h3 id="酱铺1-5人帐篷"><a href="#酱铺1-5人帐篷" class="headerlink" title="酱铺1.5人帐篷"></a>酱铺1.5人帐篷</h3><ul><li>优势<ul><li>重量在考虑范围内</li><li>价格相对合理</li></ul></li><li>劣势<ul><li>还没有看到具体设计细节,不好做出评价</li></ul></li></ul><h2 id="零碎"><a href="#零碎" class="headerlink" title="零碎"></a>零碎</h2><h3 id="水具"><a href="#水具" class="headerlink" title="水具"></a>水具</h3><ul><li>可用农夫山泉的高级矿泉水瓶代替鸭嘴兽水袋和钛水壶</li></ul><h3 id="打包"><a href="#打包" class="headerlink" title="打包"></a>打包</h3><ul><li><p>不把食物都装在一个大袋子里,而且根据每天的量分开为多个袋子,应该可以减小对空间的浪费</p></li><li><p>把neoair xlite折叠后靠背负放置</p></li><li><p>带更小的帐篷,不带巨大的金字塔,这就意味着需要升级帐篷</p></li></ul><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><h3 id="2020-10-20"><a href="#2020-10-20" class="headerlink" title="2020.10.20"></a>2020.10.20</h3><p>先升级帐篷,之后再整体打算</p>]]></content>
<summary type="html"><h2 id="背包"><a href="#背包" class="headerlink" title="背包"></a>背包</h2><h3 id="sixmoondesign的swift-X和minimalism"><a href="#sixmoondesign的swift-X</summary>
</entry>
<entry>
<title>2020年国庆亚丁徒步总结</title>
<link href="http://horsefaced.github.io/2020/10/15/2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/"/>
<id>http://horsefaced.github.io/2020/10/15/2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/</id>
<published>2020-10-15T07:07:40.000Z</published>
<updated>2020-10-18T07:25:14.722Z</updated>
<content type="html"><![CDATA[<h2 id="Apple-Watch-和-WorkOutDoors"><a href="#Apple-Watch-和-WorkOutDoors" class="headerlink" title="Apple Watch 和 WorkOutDoors"></a>Apple Watch 和 WorkOutDoors</h2><p>之前主要使用手机配合Garmin 935进行户外导航。手机上下载离线地图和轨迹,Garmin 935上本身也有提供轨迹的功能,可以见<a href="./2018-05-6-Garmin-forerunner-935-%E6%B7%BB%E5%8A%A0%E5%AF%BC%E8%88%AA%E8%B7%AF%E5%BE%84">Garmin-forerunner-935-添加导航路径</a>一文。手表导航确实有用,贡嘎那次就因为手表导航中偏离航线报警,从而避免了一次无意义的过河。这次手表换成了Apple Watch Series 5,导航就成了问题。手表导航的好处在于随手就可以看到,而手机还需要停下来认真看。经过查找,参考<a href="https://www.youtube.com/watch?v=qOh8r43XctI">Apple Watch Hiking & Backpacking Review</a>,里面推荐了<a href="https://www.youtube.com/watch?v=qOh8r43XctI">WorkOutDoors</a>这个软件。</p><p>WorkOutDoors这个软件本身可以认为是一个功能比较完善的户外导航软件。</p><ol><li><p>它可以定制表盘,显示你需要的户外徒步中显示的数据</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_3CDDD0F734F2-1.jpeg" alt="IMG_3CDDD0F734F2-1"></p></li><li><p>它可以导入gpx的航迹</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_94C38119F7FA-1.jpeg" alt="IMG_94C38119F7FA-1"></p></li><li><p>它可以下载离线地图,而且这个地图应该是优化过的,体积非常小</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_2F20EB3D7705-1.jpeg" alt="IMG_2F20EB3D7705-1"></p></li></ol><p>在手机上导入的航迹、下载的离线地图都可以直接发送到Apple Watch中,得益于S5处理器的强大,运行和操作非常流畅。</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_2390.PNG" alt="IMG_2390"></p><p>在这次亚丁转山的过程中,手机的导航基本没有使用,一直是通过手表上的导航,手机的电量得到了最大化的保存。虽然WorkOutDoors在Apple Watch上大约只能使用八到十个小时,但是对于一般情况下的徒步是足够了。只是这个软件有个bug,最后在使用完时,需要手动关闭并保存数据,如果中途手表没有电了,那么你这几个小时的数据也就没有了,我就是因为这个原因,中途有一天的数据没有了。</p><blockquote><p>可以看到四号的数据不见了</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_7D4358217882-1.jpeg" alt="IMG_7D4358217882-1"></p></blockquote><p>Apple Watch本来就是要一天一充的,在使用WorkOutDoors时就需要每天早上充满电,才可以完好的在徒步中为你服务,并在结束时完好地保存数据。</p><h2 id="lii-gear-凯夫拉挎包"><a href="#lii-gear-凯夫拉挎包" class="headerlink" title="lii gear 凯夫拉挎包"></a>lii gear 凯夫拉挎包</h2><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/939952503928844820200306225129312438.jpg" alt="939952503928844820200306225129312438"></p><p>lii gear MUSSTTE BAG 凯夫拉版本逼格非常的高。但这次户外中发现,这高逼格的东西其实并不合适自己。这个挎包本身偏机能风,在城里使用是挺好的,但是对于我这种走垃圾轻量化路线的人来说整体偏重。而且更严重的是,它的肩带调节需要额外的一根“winner winner chicken dinner”的带子才会比较容易的进行,这就莫名的添加了重量,如果不带这个根带子,那么调节长度就变得非常麻烦。</p><h2 id="酱铺防潮垫"><a href="#酱铺防潮垫" class="headerlink" title="酱铺防潮垫"></a>酱铺防潮垫</h2><blockquote><p>垃圾佬的背影</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/beiyin.png" alt="背影"></p></blockquote><p>上图中那个黑粗的就是酱铺的防潮垫。这个垫子是真的轻,整体只有230克。在使用的过程中这么轻的垫子耐操度还可以,过杜鹃林时各种剐蹭都没有破,而且保暖性也相当的不错。</p><p>前面提到过杜鹃林时各种剐蹭,这就是我不再使用它的原因。个人比较喜欢整体清爽而紧凑的打包,这种包外挂着一个巨大的防潮垫的做法真是我不喜,并且剐蹭对于徒步的流畅性来说也是挺有影响。同时这么高的包也不好使用雨伞,而对于除非非常必要不喜欢冲锋衣和雨衣的我来说,这点也是不能忍受。于是在徒步结束后,这个防潮垫就被我放弃在稻城的旅馆中了。</p><p>之后还是用回NeoAir Xlite,有可能考虑添加一个迪卡侬的折叠防潮垫作为屁垫使用,只是这样子重量不免增加不少。</p><h2 id="酱铺摩尔背架"><a href="#酱铺摩尔背架" class="headerlink" title="酱铺摩尔背架"></a>酱铺摩尔背架</h2><p>一直以来,都是使用酱铺背架的完全体,重型腰带、背部弓板、摩尔包、大河防水袋。这套用了几年,毕竟用高上限的背包背轻的东西会更舒服。但这是真的吗?这几年我五天左右的徒步最重其实都没有超过12公斤,而这其中背包就要占用1.3到1.5公斤左右。虽然1:10已经是不错的重量比,但自我感觉已经到了一个可以更轻的时间点了。</p><h2 id="太阳能板"><a href="#太阳能板" class="headerlink" title="太阳能板"></a>太阳能板</h2><p>高原的太阳再次证明了自己的威力,这次天气不太好,太阳总在厚厚的云层中偶尔才会露出来,而到了营地基本就是下雨。</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_2244.jpeg" alt="IMG_2244"></p><p>就算是这样子,太阳能板依然能每天把小米一万毫安的充电宝续命一格电,五天下来每天冲手机充手表充头灯,最后还有一格电足够从景区续命到稻城住下,以后去西部太阳能板看来是必带的了。</p><p>不过这次有个问题就是线损的存在。本次使用的是一根一米长的紫米type-c充电线,通过type-c转MicroUSB、type-c转Lightning,USB转type-c的转接器对接各种需要充电的电器。说来十分方便,但是用手按在转接头上就知道了,发热很大,说明有很大一部分能量浪费掉了。所以下次一方面要把一米的线换成更短的线,还要把转接头换成专门的线来提高效率。</p><h2 id="酒精炉"><a href="#酒精炉" class="headerlink" title="酒精炉"></a>酒精炉</h2><p>这次除了在贡嘎扎则外,全程使用酒精,在只烧水泡山屋、尾西、咖啡、苏伯汤的情况下,全程用了大约600ml的酒精,就标准来说用量挺大,主要是这次使用的小姜手做的酒精炉。这个炉子的挺好,燃烧的不会过快,效率挺高的。但是缺点也很大,就是不好掌握酒精的用量,而且剩余的酒精也没有办法回收。这次就是在加一次不够的情况下,再加一次酒精最后一定会过多,但是没有办法回收这部分酒精,只能在炉体冷却的过程中自然挥发掉,挺浪费的。</p><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/IMG_E35F8C47D2FD-1.jpeg" alt="IMG_E35F8C47D2FD-1"></p><p>在徒步前的准备阶段,从重量考虑一般会把要吃的食物从包装袋中取出来放入更轻的密封袋中。但在2018年徒步熬太的过程中,我把山之厨保留了一包,然后全程使用这包的包装袋当作锅和碗。泡山之厨、泡尾西、泡苏伯汤,完全没有问题,而且还不需要洗碗。这次没有这么做,明显麻烦了很多。锅就一个,泡了山屋就不能烧水,要吃完饭喝苏伯汤的话还要在起火,而且吃完了还要洗,所以以后还是要留一个包装袋做碗,而且保温瓶还是带有单独盖子的,梦重力轻是很轻,但是保温明显不行。</p><h2 id="食物"><a href="#食物" class="headerlink" title="食物"></a>食物</h2><p><img src="/images/2020-10-15-2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E4%BA%9A%E4%B8%81%E5%BE%92%E6%AD%A5%E6%80%BB%E7%BB%93/FullSizeRender.jpeg" alt="FullSizeRender"></p><ul><li>冻干鸡胸肉不好吃,太干太柴了,下次不带了</li><li>网易严选牛肉不错,放在家里也是过期,下次可以考虑</li><li>在香格里拉镇买的鱼皮、鱼干实在太油腻了,特别是鱼皮,吃了上火,以后要坚决避免买这种上火的东西当路粮。</li><li>下次可以试试带家里晒干的小鱼干</li><li>都是腰果太单调,下次换一下沃尔玛的综合坚果试试</li><li>柠檬味士力架天冷吃很不错,柠檬味在城里吃非常的香精,但是在户外吃还挺清爽的</li><li>黑芝麻糊作为中午路粮很好,每天路上都盼望着那个吃黑芝麻糊的时刻</li><li>雀巢的冻干咖啡味道很好,能比较好的还原咖啡香味和口味,下次可以继续带</li><li>苏伯汤配上脱水蔬菜能很好的补充纤维的不足,也能满足心理的需要</li><li>下次带点善存等复合维生素片,这次没带上火了,而且手上的伤不容易好</li><li>下次记得携带宝矿力活着其他运动饮料冲泡剂</li></ul><h3 id="数量问题"><a href="#数量问题" class="headerlink" title="数量问题"></a>数量问题</h3><ul><li><p>这次带了24块士力架,最后剩余11块,平均一天两块,但实际上在强度比较大的时候,一天会用到3块至4块,以后可以照平均一天4块进行携带,同时减少其他零食的数量。</p></li><li><p>这次带了500g的腰果,路途中和营地都有吃,最后剩余200g左右,说明500g腰果能支撑8天左右的路程。</p></li><li><p>冻干咖啡可以考虑照一天两条的数量进行携带,以支持中午13点至15点间的体力低潮</p></li><li><p>可以考虑携带猪肉圃</p></li><li><p>24根士力架 剩余11条</p></li><li><p>5包网易牛肉干 吃完</p></li><li><p>500克腰果 吃完</p></li><li><p>5份黑芝麻糊 吃完</p></li><li><p>50克干鸡胸肉 剩余40克</p></li><li><p>50克脱水蔬菜 剩余15克</p></li><li><p>10包苏伯汤 吃完</p></li><li><p>5份速溶咖啡 吃完</p></li><li><p>2份姜糖 吃完</p></li><li><p>5份山屋 吃完</p></li><li><p>5份尾西 吃完</p></li><li><p>一份意大利面 吃完</p></li></ul>]]></content>
<summary type="html"><h2 id="Apple-Watch-和-WorkOutDoors"><a href="#Apple-Watch-和-WorkOutDoors" class="headerlink" title="Apple Watch 和 WorkOutDoors"></a>Apple Wa</summary>
<category term="户外" scheme="http://horsefaced.github.io/tags/%E6%88%B7%E5%A4%96/"/>
</entry>
<entry>
<title>如何在.NET Core上建立Agora的AccessToken服务</title>
<link href="http://horsefaced.github.io/2020/09/23/%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/"/>
<id>http://horsefaced.github.io/2020/09/23/%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/</id>
<published>2020-09-23T11:20:48.000Z</published>
<updated>2020-09-23T11:59:29.078Z</updated>
<content type="html"><![CDATA[<p>随着大量公司将办公会议、产品发布等改为网上进行后, 视频通信的安全性越来越成为受关注的重点。声网平台在 2.1.0 版本之后,通过使用 AccessToken 认证统一了视频通话RTC、录制、消息通讯RTM等各 SDK 的安全认证形式,相较于原先的 DynmicKey 更加方便于用户使用。</p><p>.NET Core 是微软的跨平台开发框架,可运行在 Windows、Linux、macOS 等操作系统之上,通过命令行工具就可以方便的创建、编译、运行,并可搭配 <a href="https://docs.microsoft.com/zh-cn/dotnet/core/docker/introduction">Docker 容器</a>使用,方便嵌入微服务架构中。</p><p>本文将基于 .NET Core 3.1 版本说明如何建立一个 Agora RTC Token 服务,同样这个服务也可以用于录制和 RTM SDK中。</p><h2 id="预备知识"><a href="#预备知识" class="headerlink" title="预备知识"></a>预备知识</h2><ul><li>本文默认读者了解基本的 C# 编程知识,如果有需要可以访问<a href="https://docs.microsoft.com/zh-cn/dotnet/csharp/">C#文档</a> 进行了解。</li><li>本文需要 ASP.NET Core 及相关的 WebAPI 知识,如果有需要可以访问<a href="https://docs.microsoft.com/zh-cn/aspnet/core/?view=aspnetcore-3.1">ASP.NET 文档</a>进行了解。</li><li>本文会有一点点 Git 相关的使用,但不是必要的。</li></ul><h2 id="本文所需工具"><a href="#本文所需工具" class="headerlink" title="本文所需工具"></a>本文所需工具</h2><ul><li><a href="https://dotnet.microsoft.com/download">.NET Core SDK</a> - 包括 .NET Core 运行时、开发包及命令行工具。</li><li><a href="https://code.visualstudio.com/">Visual Studio Code</a> - 微软推出的跨平台开发工具,你也可以使用自己喜欢或习惯的开发工具。</li><li><a href="https://code.visualstudio.com/docs/languages/dotnet">.NET Core开发环境配置</a> - 如果你刚开始使用 Visual Studio Code,推荐阅读这个链接中的安装配置。</li><li><a href="https://git-scm.com/downloads">Git</a> - 本文会使用到 Git 但不是必要条件,在相应章节会进行说明。</li></ul><h2 id="项目创建"><a href="#项目创建" class="headerlink" title="项目创建"></a>项目创建</h2><ol><li>打开终端,进入你平时开发目录</li><li>运行以下命令</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">dotnet new webapi -o AgoraTokenServer</span><br><span class="line">code -r AgoraTokenServer</span><br></pre></td></tr></table></figure><ol start="3"><li><p>如果你正确的安装了 Visual Studio Code 的话,这时系统应该会打开 Visual Studio Code 程序并将 AgoraTokenServer 这个项目显示在左侧,如下图所示:</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913165213034.png"></p><p>为了方便起见,以下 Visual Studio Code 将简称为 vscode。此时整个项目的目录结构应该如下图所示:</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913165358515.png"></p></li></ol><p>我们将 WeatherForecast.cs 与 Controllers/WeatherForecastController.cs 删除,稍后我们将建立起自己的服务。</p><h2 id="开发"><a href="#开发" class="headerlink" title="开发"></a>开发</h2><h3 id="引入工具代码"><a href="#引入工具代码" class="headerlink" title="引入工具代码"></a>引入工具代码</h3><p>Agora 在其<a href="https://github.com/AgoraIO/Tools">AgoraIO in GitHub</a>中提供了 AccessToken 的 C# 实现,我们可以直接使用它。</p><ol><li>进入<a href="https://github.com/AgoraIO/Tools">AgoraIO in GitHub</a>,点击页面上那个绿色的Code按钮</li></ol><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913170057183.png"></p><ol start="2"><li>如果你会 Git 那么可以直接在其他目录中,<strong>注意不要直接在上一章节建立的 AgoraTokenServer 项目目录中</strong>,把项目克隆下来。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/AgoraIO/Tools.git</span><br></pre></td></tr></table></figure><p> 如果你不会 Git ,可以直接点击 Download ZIP 将其下载下来并解压缩。</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913172902977.png"></p><ol start="3"><li><p>进入刚刚 Git 克隆或者下载解压缩后的目录</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> Tools/DynamicKey/AgoraDynamicKey/csharp/src/AgoraIO</span><br></pre></td></tr></table></figure><p>将其中的 Common、Extensions、Media、Utils 四个目录直接拷贝至你创建的 AgoraTokenServer 目录下,之后你的 AgoraTokenServer 目录结构应该是如下图这样子的:</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913174340000.png"></p></li></ol><h3 id="解决依赖"><a href="#解决依赖" class="headerlink" title="解决依赖"></a>解决依赖</h3><p>你会发现上图中 Media/AccessToken.cs 是红色的,那是因为这个项目依赖于<a href="https://www.nuget.org/packages/Crc32.NET/">Crc32.NET</a>这个包,如果你正确的安装了 .NET Core 的运行时和命令行工具的话 我们直接使用命令行将其安装就可以了。</p><p>进入 AgoraTokenServer 项目的根目录下,运行如下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet add package Crc32.NET</span><br></pre></td></tr></table></figure><p>这样子我们唯一一个外部依赖包就解决了。</p><h3 id="设置-AppID-与-AppCertificate"><a href="#设置-AppID-与-AppCertificate" class="headerlink" title="设置 AppID 与 AppCertificate"></a>设置 AppID 与 AppCertificate</h3><ol><li><p>在通常环境中 AppCertificate 应当保存在安全性较高的服务端,不宜通过客户端请求进行传输,在 .NET Core 中这种设置通常可以保存在 <strong>appsetting.json</strong> 中。<em>下面 appsetting.json 代码中的 AppID 和 AppCertificate 为示例,请在使用中替换为自己使用的对应 AppID 和 AppCertificate。</em></p><figure class="highlight json"><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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"AppSettings"</span>: {</span><br><span class="line"> <span class="attr">"AppID"</span>: <span class="string">"970CA35de60c44645bbae8a215061b33"</span>,</span><br><span class="line"> <span class="attr">"AppCertificate"</span>: <span class="string">"5CFd2fd1755d40ecb72977518be15d3b"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"Logging"</span>: {</span><br><span class="line"> <span class="attr">"LogLevel"</span>: {</span><br><span class="line"> <span class="attr">"Default"</span>: <span class="string">"Information"</span>,</span><br><span class="line"> <span class="attr">"Microsoft"</span>: <span class="string">"Warning"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Hosting.Lifetime"</span>: <span class="string">"Information"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"AllowedHosts"</span>: <span class="string">"*"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>建立配置类</p><p>在 Utils 目录下创建一个名为 AppSettings.cs 的文件,文件内容为:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer.Utils</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AppSettings</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> AppID { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> AppCertificate { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>注入配置类</p><p>ASP.NET Core 使用依赖注入来解决整个程序的依赖问题,通过这个机制我们可以很方便的把上面定义的配置注入进去。依赖注入需要在 Startup.cs 文件中添加自定义的配置类,添加后 Startup.cs 文件内容如下:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> AgoraTokenServer.Utils;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Builder;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Configuration;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Hosting;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Startup</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Startup</span>(<span class="params">IConfiguration configuration</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> Configuration = configuration;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> IConfiguration Configuration { <span class="keyword">get</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// This method gets called by the runtime. Use this method to add services to the container.</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> services.AddCors(); <span class="comment">//添加跨域请求</span></span><br><span class="line"> services.AddControllers();</span><br><span class="line"> services.Configure<AppSettings>(Configuration.GetSection(<span class="string">"AppSettings"</span>)); <span class="comment">//添加程序配置</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IWebHostEnvironment env</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">if</span> (env.IsDevelopment())</span><br><span class="line"> {</span><br><span class="line"> app.UseDeveloperExceptionPage();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> app.UseHttpsRedirection();</span><br><span class="line"></span><br><span class="line"> app.UseRouting();</span><br><span class="line"></span><br><span class="line"> app.UseCors(x => x</span><br><span class="line"> .AllowAnyOrigin()</span><br><span class="line"> .AllowAnyMethod()</span><br><span class="line"> .AllowAnyHeader());</span><br><span class="line"></span><br><span class="line"> app.UseAuthorization();</span><br><span class="line"></span><br><span class="line"> app.UseEndpoints(endpoints =></span><br><span class="line"> {</span><br><span class="line"> endpoints.MapControllers();</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></li></ol><h3 id="创建-Model"><a href="#创建-Model" class="headerlink" title="创建 Model"></a>创建 Model</h3><p>我们先定义两个对象来描述接受的内容和返回的结果。首先建立一个名为 Models 的目录,再在目录下创建两个文件。</p><ol><li><p>请求对象文件</p><p><strong>Path: /Models/AuthenticateRequest.cs</strong></p><p> 在 Models 目录下创建 AuthenticateRequest.cs 文件,文件内容为:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.ComponentModel.DataAnnotations;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer.Models</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AuthenticateRequest</span></span><br><span class="line"> {</span><br><span class="line"> [<span class="meta">Required</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> channel { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> [<span class="meta">Required</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">dynamic</span> uid { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">uint</span> expiredTs { <span class="keyword">get</span>; <span class="keyword">set</span>; } = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> role { <span class="keyword">get</span>; <span class="keyword">set</span>; } = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>因为 Agora 的用户标识有两种类型,一种是 uint 型,一种是 string 型的,所以这里直接使用 dynamic 类型来同时兼容两种类型。</p></li><li><p>回应对象</p><p><strong>Path: /Models/AuthenticateResponse.cs</strong></p><p>在 Models 目录下创建 AuthenticateResponse.cs 文件,文件内容为:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer.Models</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AuthenticateResponse</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> channel { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">dynamic</span> uid { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> token { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>现在项目的结构如下图:</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913194237623.png"></p></li></ol><h3 id="创建服务"><a href="#创建服务" class="headerlink" title="创建服务"></a>创建服务</h3><ol><li><p>现在我们创建一个控制器来提供服务,首先在 AgoraTokenServer 项目的 Controllers 目录下建立一个名为 AccessTokenController.cs 的文件。</p><p><strong>Path: /Controllers/AccessTokenController.cs</strong></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> AgoraTokenServer.Models;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Mvc;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer.Contollers</span></span><br><span class="line">{</span><br><span class="line"> [<span class="meta">ApiController</span>]</span><br><span class="line"> [<span class="meta">Route(<span class="meta-string">"[controller]"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AccessTokenController</span> : <span class="title">ControllerBase</span></span><br><span class="line"> {</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>添加程序配置</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> AgoraTokenServer.Utils;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Mvc;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Options;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer.Contollers</span></span><br><span class="line">{</span><br><span class="line"> [<span class="meta">ApiController</span>]</span><br><span class="line"> [<span class="meta">Route(<span class="meta-string">"[controller]"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AccessTokenController</span> : <span class="title">ControllerBase</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> AppSettings appSettings;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">AccessTokenController</span>(<span class="params">IOptions<AppSettings> appSettings</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">this</span>.appSettings = appSettings.Value;</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></li><li><p>添加请求处理部分</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.Net;</span><br><span class="line"><span class="keyword">using</span> System.Text.Json;</span><br><span class="line"><span class="keyword">using</span> AgoraIO.Media;</span><br><span class="line"><span class="keyword">using</span> AgoraTokenServer.Models;</span><br><span class="line"><span class="keyword">using</span> AgoraTokenServer.Utils;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Mvc;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Options;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AgoraTokenServer.Contollers</span></span><br><span class="line">{</span><br><span class="line"> [<span class="meta">ApiController</span>]</span><br><span class="line"> [<span class="meta">Route(<span class="meta-string">"[controller]"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AccessTokenController</span> : <span class="title">ControllerBase</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> AppSettings appSettings;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">AccessTokenController</span>(<span class="params">IOptions<AppSettings> appSettings</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">this</span>.appSettings = appSettings.Value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> [<span class="meta">HttpPost</span>]</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ActionResult<AuthenticateResponse> <span class="title">index</span>(<span class="params">AuthenticateRequest request</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">string</span>.IsNullOrEmpty(appSettings.AppID) || <span class="keyword">string</span>.IsNullOrEmpty(appSettings.AppCertificate))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> StatusCodeResult((<span class="keyword">int</span>)HttpStatusCode.PreconditionFailed);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> uid = request.uid.ValueKind == JsonValueKind.Number ? request.uid.GetUInt64().ToString() : request.uid.GetString();</span><br><span class="line"> <span class="keyword">var</span> tokenBuilder = <span class="keyword">new</span> AccessToken(appSettings.AppID, appSettings.AppCertificate, request.channel, uid);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kJoinChannel, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kPublishAudioStream, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kPublishVideoStream, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kPublishDataStream, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kRtmLogin, request.expiredTs);</span><br><span class="line"> <span class="keyword">return</span> Ok(<span class="keyword">new</span> AuthenticateResponse</span><br><span class="line"> {</span><br><span class="line"> channel = request.channel,</span><br><span class="line"> uid = request.uid,</span><br><span class="line"> token = tokenBuilder.build()</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><p>在请求处理中,直接调用了从 AgoraIO 上下载的代码,并且在没有配置 AppID 和 AppCertificate 情况下会回报 412 错误。</p><p>同时,这个示例代码中直接将[kJoinChannel, kPublishAudioStream, kPublishVideoStream, kPubishDataStream, kRtmLogin] 的权限一次性给出来,你可以根据直接的需要,在 AuthenticateRequest 中添加权限申请的字段, 实现权限的申请功能。</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></pre></td><td class="code"><pre><span class="line">[<span class="meta">HttpPost</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> ActionResult<AuthenticateResponse> <span class="title">index</span>(<span class="params">AuthenticateRequest request</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">string</span>.IsNullOrEmpty(appSettings.AppID) || <span class="keyword">string</span>.IsNullOrEmpty(appSettings.AppCertificate))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> StatusCodeResult((<span class="keyword">int</span>)HttpStatusCode.PreconditionFailed);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> uid = request.uid.ValueKind == JsonValueKind.Number ? request.uid.GetUInt64().ToString() : request.uid.GetString();</span><br><span class="line"> <span class="keyword">var</span> tokenBuilder = <span class="keyword">new</span> AccessToken(appSettings.AppID, appSettings.AppCertificate, request.channel, uid);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kJoinChannel, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kPublishAudioStream, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kPublishVideoStream, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kPublishDataStream, request.expiredTs);</span><br><span class="line"> tokenBuilder.addPrivilege(Privileges.kRtmLogin, request.expiredTs);</span><br><span class="line"> <span class="keyword">return</span> Ok(<span class="keyword">new</span> AuthenticateResponse</span><br><span class="line"> {</span><br><span class="line"> channel = request.channel,</span><br><span class="line"> uid = request.uid,</span><br><span class="line"> token = tokenBuilder.build()</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><h3 id="编译并运行"><a href="#编译并运行" class="headerlink" title="编译并运行"></a>编译并运行</h3><p>.NET Core 的编译和运行只需要通过命令行既可以解决,在 AgoraTokenServer 目录下,直接在命令行中运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet build</span><br></pre></td></tr></table></figure><p>就可以编译整个工程了。</p><p>运行也很直接,直接在命令行中运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet run</span><br></pre></td></tr></table></figure><p>就可以在 <a href="https://localhost:5001/">https://localhost:5001</a> 和 <a href="http://localhost:5000/">http://localhost:5000</a> 上运行服务了。</p><p>如果你想改缺省的运行端口,推荐直接修改 <strong>Path: /Properties/launchSettings.json</strong> 文件中的 AgoraTokenServer 这一节的 applicationUrl 参数,其内容如下:</p><figure class="highlight"><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></pre></td><td class="code"><pre><span class="line">"AgoraTokenServer": {</span><br><span class="line"> "commandName": "Project",</span><br><span class="line"> "launchBrowser": true,</span><br><span class="line"> "launchUrl": "weatherforecast",</span><br><span class="line"> "applicationUrl": "https://localhost:5001;http://localhost:5000",</span><br><span class="line"> "environmentVariables": {</span><br><span class="line"> "ASPNETCORE_ENVIRONMENT": "Development"</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>因为修改过的 launchSettings.json 本身也会做为一个配置文件发布在最终运行目录中,这样子就不用吧端口写死在源代码中,或者在 Program.cs 中额外添加代码了。</p><h2 id="测试结果"><a href="#测试结果" class="headerlink" title="测试结果"></a>测试结果</h2><p>在本文中,使用<a href="www.postman.com">Postman</a>对服务进行测试,大家可以使用自己习惯的工具。在具体的请求中,因为 expiredTS 和 role 在程序中有缺省值,所以请求中就可以忽略,并且在现阶段,role 只有一个值,所以推荐可以暂时忽略这个。而 expiredTS 的具体用法,可以参考Agora官方网站的<a href="https://docs.agora.io/cn/Video/token_server_cpp?platform=CPP">生成Token</a>一文中的说明。</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913214230336.png"></p><p>具体的 Postman 请求结果如下图所示。</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913213711509.png"></p><p>如果你在使用 Postman 发送请求的时候发生了下图的错误:</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913214838643.png"></p><p>是因为你现在访问的 https 链接使用的证书是无效的,实际使用中你需要部署真实的证书,测试阶段你可以通过下图的 Settings 按钮将第一个 Enable SSL certificate verification 关闭</p><p><img src="/images/2020-09-23-%E5%A6%82%E4%BD%95%E5%9C%A8-NET-Core%E4%B8%8A%E5%BB%BA%E7%AB%8BAgora%E7%9A%84AccessToken%E6%9C%8D%E5%8A%A1/image-20200913215607065.png"></p><h2 id="完成"><a href="#完成" class="headerlink" title="完成"></a>完成</h2><p>到现在为止,基于 .NET Core 的 Agora Token 服务已经开发完成。在实际使用中,还需要添加安全机制,这个可以根据你自己的具体架构情况进行完善。</p><p>.NET Core 的 docker 化可以参考微软的 <a href="https://docs.microsoft.com/zh-cn/dotnet/core/docker/introduction">Docker 容器</a> 这编文章。</p><p>本文的所有代码都可以在 <a href="https://github.com/horsefaced/AgoraTokenServer-For-NET-Core">GitHub</a> 上下载。</p>]]></content>
<summary type="html"><p>随着大量公司将办公会议、产品发布等改为网上进行后, 视频通信的安全性越来越成为受关注的重点。声网平台在 2.1.0 版本之后,通过使用 AccessToken 认证统一了视频通话RTC、录制、消息通讯RTM等各 SDK 的安全认证形式,相较于原先的 DynmicKey 更加</summary>
</entry>
<entry>
<title>2020年国庆装备表</title>
<link href="http://horsefaced.github.io/2020/09/06/2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E8%A3%85%E5%A4%87%E8%A1%A8/"/>
<id>http://horsefaced.github.io/2020/09/06/2020%E5%B9%B4%E5%9B%BD%E5%BA%86%E8%A3%85%E5%A4%87%E8%A1%A8/</id>
<published>2020-09-06T06:24:43.000Z</published>
<updated>2022-03-10T02:21:33.072Z</updated>
<content type="html"><![CDATA[<table><thead><tr><th>类别</th><th>名称</th><th>数量</th><th>重量</th><th>Check</th></tr></thead><tbody><tr><td>三大件</td><td>田野睡袋</td><td>1</td><td>872</td><td>OK</td></tr><tr><td></td><td>酱铺的泡沫垫</td><td>1</td><td>230</td><td></td></tr><tr><td></td><td>AMK Bivy 地布</td><td>1</td><td>101</td><td>OK</td></tr><tr><td></td><td>酱铺二人金字塔</td><td>1</td><td>581</td><td>OK</td></tr><tr><td></td><td>酱铺背架(简易版)</td><td>1</td><td>1010</td><td>OK</td></tr><tr><td></td><td>钛空心地丁</td><td>15</td><td>137</td><td>OK</td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td>炊具</td><td>小姜手作酒精炉</td><td>1</td><td>15</td><td>OK</td></tr><tr><td></td><td>bushbuddy柴火炉</td><td>1</td><td>181</td><td>OK</td></tr><tr><td></td><td>铝锅</td><td>1</td><td>96</td><td>OK</td></tr><tr><td></td><td>Flod-A-Cup 折叠水杯</td><td>1</td><td>50</td><td>OK</td></tr><tr><td></td><td>3升康迪净水水袋</td><td>1</td><td>102</td><td>OK</td></tr><tr><td></td><td>象印梦重力500</td><td>1</td><td>194</td><td>OK</td></tr><tr><td></td><td>鸭嘴兽1升水袋(奶头盖子)</td><td>1</td><td>35</td><td>OK</td></tr><tr><td></td><td>勺子</td><td>1</td><td></td><td>OK</td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td>衣服</td><td>smartwood袜子</td><td>1</td><td>68</td><td></td></tr><tr><td></td><td>速干长裤</td><td>1</td><td>320</td><td>OK</td></tr><tr><td></td><td>羽绒裤</td><td>1</td><td>260</td><td>OK</td></tr><tr><td></td><td>冲锋裤</td><td>1</td><td>72</td><td>OK</td></tr><tr><td></td><td>老鼠神衣</td><td>1</td><td>272</td><td>OK</td></tr><tr><td></td><td>始祖鸟冲锋衣</td><td>1</td><td>340</td><td>OK</td></tr><tr><td></td><td>羽绒服</td><td>1</td><td>340</td><td>OK</td></tr><tr><td></td><td>羊毛帽子</td><td>1</td><td>78</td><td>OK</td></tr><tr><td></td><td>buff太阳帽</td><td>1</td><td>30</td><td></td></tr><tr><td></td><td>羊毛内衣</td><td>1</td><td>202</td><td>OK</td></tr><tr><td></td><td>羊毛内裤</td><td>1</td><td>168</td><td>OK</td></tr><tr><td></td><td>内裤</td><td>1</td><td>40</td><td>OK</td></tr><tr><td></td><td>羽绒脚套</td><td>1</td><td>150</td><td>OK</td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td>电器</td><td>小米一万毫安</td><td>1</td><td>226</td><td>OK</td></tr><tr><td></td><td>Apple Watch充电器</td><td>1</td><td>24</td><td>OK</td></tr><tr><td></td><td>紫米Type-C充电线+Lightning转接头+microUSB转接头</td><td>1</td><td>30</td><td>OK</td></tr><tr><td></td><td>Apple Watch 5</td><td>1</td><td>63</td><td></td></tr><tr><td></td><td>iPhone 11</td><td>1</td><td>220</td><td></td></tr><tr><td></td><td>充电器</td><td>1</td><td>43</td><td>OK</td></tr><tr><td></td><td>头灯</td><td>1</td><td>34</td><td>OK</td></tr><tr><td></td><td>伏来阳太阳能板</td><td>1</td><td>207</td><td>OK</td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td>杂项</td><td>牙刷牙膏毛巾</td><td>1</td><td>90</td><td>OK</td></tr><tr><td></td><td>眼镜</td><td>1</td><td>23</td><td>OK</td></tr><tr><td></td><td>救生哨、打火棒</td><td>1</td><td>33</td><td>OK</td></tr><tr><td></td><td>药品</td><td>1</td><td></td><td>OK</td></tr><tr><td></td><td>睡觉耳塞</td><td>1</td><td></td><td>OK</td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr></tbody></table>]]></content>
<summary type="html"><table>
<thead>
<tr>
<th>类别</th>
<th>名称</th>
<th>数量</th>
<th>重量</th>
<th>Check</th>
</tr>
</thead>
<tbody><tr>
<td>三大件</td>
<td>田野睡袋</td>
<t</summary>
<category term="户外" scheme="http://horsefaced.github.io/tags/%E6%88%B7%E5%A4%96/"/>
</entry>
<entry>
<title>artemis的消息持久问题</title>
<link href="http://horsefaced.github.io/2020/03/27/artemis%E7%9A%84%E6%B6%88%E6%81%AF%E6%8C%81%E4%B9%85%E9%97%AE%E9%A2%98/"/>
<id>http://horsefaced.github.io/2020/03/27/artemis%E7%9A%84%E6%B6%88%E6%81%AF%E6%8C%81%E4%B9%85%E9%97%AE%E9%A2%98/</id>
<published>2020-03-27T07:05:41.000Z</published>
<updated>2020-03-27T07:24:56.722Z</updated>
<content type="html"><![CDATA[<p>根据<a href="https://activemq.apache.org/how-does-a-queue-compare-to-a-topic">How does a Queue compare to a Topic</a>的介绍, 以下是原文:</p><blockquote><p>Topics<br>In JMS a Topic implements publish and subscribe semantics. When you publish a message it goes to all the subscribers who are interested - so zero to many subscribers will receive a copy of the message. Only subscribers who had an active subscription at the time the broker receives the message will get a copy of the message.</p></blockquote><blockquote><p>Queues<br>A JMS Queue implements load balancer semantics. A single message will be received by exactly one consumer. If there are no consumers available at the time the message is sent it will be kept until a consumer is available that can process the message. If a consumer receives a message and does not acknowledge it before closing then the message will be redelivered to another consumer. A queue can have many consumers with messages load balanced across the available consumers.</p></blockquote><blockquote><p>So Queues implement a reliable load balancer in JMS.</p></blockquote><p>说明, 以topic形式出现的消息在没有订阅者的情况下, 并不能持久化, 而是直接丢弃了. 只有发布在queue中才能持久化. 其表现为, 通过MQTT发布的topic, 在没有特别配置的情况下, 就是说在topic与queue都是auto-create的情况下, 消息会在没有消费者的情况下直接丢失. </p><p>通过配置storker.xml文件, 可以指定某个address生成queue, 比如:</p><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">address</span> <span class="attr">name</span>=<span class="string">"sometopic"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">anycast</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">queue</span> <span class="attr">name</span>=<span class="string">"sometopic"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">anycast</span>></span></span><br><span class="line"><span class="tag"></<span class="name">address</span>></span></span><br></pre></td></tr></table></figure><p>这时, 发送到这个topic上的消息就可以持久化了. </p><p>但这里要注意, 如果是:</p><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">address</span> <span class="attr">name</span>=<span class="string">"sometopic"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">multicast</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">queue</span> <span class="attr">name</span>=<span class="string">"sometopic"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">multicast</span>></span></span><br><span class="line"><span class="tag"></<span class="name">address</span>></span></span><br></pre></td></tr></table></figure><p>会形成一个publish/subscribe形式, 根据上文指出的, 就不会持久化消息.</p><p>最后, mqtt缺省的就是publish/subscribe模型, 在没有订阅者的情况下就是会丢失消息.</p>]]></content>
<summary type="html"><p>根据<a href="https://activemq.apache.org/how-does-a-queue-compare-to-a-topic">How does a Queue compare to a Topic</a>的介绍, 以下是原文:</p>
<block</summary>
<category term="artemis mqtt program" scheme="http://horsefaced.github.io/tags/artemis-mqtt-program/"/>
</entry>
<entry>
<title>2019梅里装备列表</title>
<link href="http://horsefaced.github.io/2019/09/01/%E6%A2%85%E9%87%8C%E8%A3%85%E5%A4%87/"/>
<id>http://horsefaced.github.io/2019/09/01/%E6%A2%85%E9%87%8C%E8%A3%85%E5%A4%87/</id>
<published>2019-09-01T01:58:38.000Z</published>
<updated>2020-09-06T07:53:52.744Z</updated>
<content type="html"><![CDATA[<h2 id="然后没有实现-去了梅里"><a href="#然后没有实现-去了梅里" class="headerlink" title="然后没有实现, 去了梅里"></a>然后没有实现, 去了梅里</h2><table><thead><tr><th>类别</th><th>名称</th><th>数量</th><th>重量</th><th>Check</th></tr></thead><tbody><tr><td>三大件</td><td>田野睡袋</td><td>1</td><td>872</td><td>OK</td></tr><tr><td></td><td>therm-a-rest xlite+气泵</td><td>1</td><td>415</td><td>OK</td></tr><tr><td></td><td>AMK Bivy 地布</td><td>1</td><td>101</td><td>OK</td></tr><tr><td></td><td>酱铺cuben天幕加吊床</td><td>1</td><td>681</td><td>OK</td></tr><tr><td></td><td>酱铺背架</td><td>1</td><td>1010</td><td>OK</td></tr><tr><td>炊具</td><td>SOTO WindMaster</td><td>1</td><td>49</td><td>OK</td></tr><tr><td></td><td>柴火炉</td><td>1</td><td>160</td><td>OK</td></tr><tr><td></td><td>铝锅</td><td>1</td><td>96</td><td>OK</td></tr><tr><td></td><td>Flod-A-Cup 折叠水杯</td><td>1</td><td>50</td><td>OK</td></tr><tr><td></td><td>3升康迪净水水袋</td><td>1</td><td>102</td><td>OK</td></tr><tr><td></td><td>膳魔师FEK800</td><td>1</td><td>194</td><td>OK</td></tr><tr><td>衣服</td><td>smartwood袜子</td><td>1</td><td>68</td><td>OK</td></tr><tr><td></td><td>outdome袜子</td><td>1</td><td>60</td><td>OK</td></tr><tr><td></td><td>防沙套</td><td>1</td><td>40</td><td></td></tr><tr><td></td><td>速干长裤</td><td>1</td><td>320</td><td>OK</td></tr><tr><td></td><td>羽绒裤</td><td>1</td><td>260</td><td>OK</td></tr><tr><td></td><td>冲锋裤</td><td>1</td><td>268</td><td>OK</td></tr><tr><td></td><td>羊毛T恤</td><td>1</td><td>165</td><td>OK</td></tr><tr><td></td><td>老鼠神衣</td><td>1</td><td>272</td><td>OK</td></tr><tr><td></td><td>始祖鸟冲锋衣</td><td>1</td><td>340</td><td>OK</td></tr><tr><td></td><td>羽绒服</td><td>1</td><td>340</td><td>OK</td></tr><tr><td></td><td>迪卡侬手套</td><td>1</td><td>103</td><td></td></tr><tr><td></td><td>羊毛帽子</td><td>1</td><td>78</td><td>OK</td></tr><tr><td></td><td>棒球帽</td><td>1</td><td>100</td><td>OK</td></tr><tr><td></td><td>羊毛内衣</td><td>1</td><td>202</td><td>OK</td></tr><tr><td></td><td>羊毛内裤</td><td>1</td><td>168</td><td>OK</td></tr><tr><td></td><td>内裤</td><td>1</td><td>40</td><td>OK</td></tr><tr><td></td><td>羽绒胶套</td><td>1</td><td>150</td><td>OK</td></tr><tr><td>电器</td><td>小米一万毫安</td><td>1</td><td>226</td><td>OK</td></tr><tr><td></td><td>Garmin充电线</td><td>1</td><td>15</td><td>OK</td></tr><tr><td></td><td>Type-C充电线</td><td>1</td><td>30</td><td>OK</td></tr><tr><td></td><td>北斗海聊</td><td></td><td>233</td><td>OK</td></tr><tr><td></td><td>iPhone 11</td><td>1</td><td>220</td><td>OK</td></tr><tr><td></td><td>充电器</td><td>1</td><td>43</td><td>OK</td></tr><tr><td></td><td>头灯</td><td>1</td><td>34</td><td>OK</td></tr><tr><td>杂项</td><td>牙刷牙膏毛巾</td><td>1</td><td>90</td><td>OK</td></tr><tr><td></td><td>眼镜</td><td>1</td><td>23</td><td>OK</td></tr><tr><td></td><td>救生哨、打火棒</td><td>1</td><td>33</td><td>OK</td></tr><tr><td></td><td>Garmin forunner 935</td><td>1</td><td>50</td><td>OK</td></tr><tr><td></td><td>三峰bivy</td><td>1</td><td>140</td><td>OK</td></tr></tbody></table>]]></content>
<summary type="html"><h2 id="然后没有实现-去了梅里"><a href="#然后没有实现-去了梅里" class="headerlink" title="然后没有实现, 去了梅里"></a>然后没有实现, 去了梅里</h2><table>
<thead>
<tr>
<th>类别</th>
<t</summary>
<category term="户外" scheme="http://horsefaced.github.io/tags/%E6%88%B7%E5%A4%96/"/>
</entry>
<entry>
<title>华为的快速充电标准</title>
<link href="http://horsefaced.github.io/2018/07/05/%E5%8D%8E%E4%B8%BA%E7%9A%84%E5%BF%AB%E9%80%9F%E5%85%85%E7%94%B5%E6%A0%87%E5%87%86/"/>
<id>http://horsefaced.github.io/2018/07/05/%E5%8D%8E%E4%B8%BA%E7%9A%84%E5%BF%AB%E9%80%9F%E5%85%85%E7%94%B5%E6%A0%87%E5%87%86/</id>
<published>2018-07-04T22:13:22.000Z</published>
<updated>2018-07-04T22:19:20.828Z</updated>
<content type="html"><![CDATA[<p>华为手机的superCharge支持的<br>超级快充标准是 5V4.5A、4.5V5A<br>快速充电标准是 9V2A<br>普通充电标准是 5V2A 5V1A</p><p>如果充电线支持的是2A的电流,那么用原装SuperChange充电头,反而只能普通充电模式<br>充电宝要想快速充电,要支持到9V2A的输出才行,否则只能普通充电模式</p>]]></content>
<summary type="html"><p>华为手机的superCharge支持的<br>超级快充标准是 5V4.5A、4.5V5A<br>快速充电标准是 9V2A<br>普通充电标准是 5V2A 5V1A</p>
<p>如果充电线支持的是2A的电流,那么用原装SuperChange充电头,反而只能普通充电模式<br</summary>
</entry>
<entry>
<title>Garmin forerunner 935 添加导航路径</title>
<link href="http://horsefaced.github.io/2018/05/06/Garmin-forerunner-935-%E6%B7%BB%E5%8A%A0%E5%AF%BC%E8%88%AA%E8%B7%AF%E5%BE%84/"/>
<id>http://horsefaced.github.io/2018/05/06/Garmin-forerunner-935-%E6%B7%BB%E5%8A%A0%E5%AF%BC%E8%88%AA%E8%B7%AF%E5%BE%84/</id>
<published>2018-05-06T00:04:40.000Z</published>
<updated>2018-05-06T00:25:27.493Z</updated>
<content type="html"><![CDATA[<p>Garmin这个奸商,广告里不带导航功能的手表连倒入路径都不支持,就算是准旗舰的forerunner 935。但其实要倒入导航路径很简单。</p><ol><li>登录<a href="https://connectus.garmin.cn/modern/courses">Garmin Connect</a>,使用“导入”功能来把gpx路径导入到Garmin Connect中。</li></ol><p><img src="/images/garmin/course.png" alt="倒入路径"></p><ol start="2"><li>选中刚刚倒入的路径</li></ol><p><img src="/images/garmin/detail.png" alt="路径详情"></p><ol start="3"><li>下载路径为FIT格式</li></ol><p><img src="/images/garmin/download.png" alt="FIT格式"></p><ol start="4"><li><p>把手表通过USB连接到电脑上,连接模式选择大容量存储</p></li><li><p>把下载的FIT文件拷贝到如图的“COURSES”目录下</p></li></ol><p><img src="/images/garmin/directory.png" alt="目录"></p><ol start="6"><li>Well done, and enjoy!</li></ol>]]></content>
<summary type="html"><p>Garmin这个奸商,广告里不带导航功能的手表连倒入路径都不支持,就算是准旗舰的forerunner 935。但其实要倒入导航路径很简单。</p>
<ol>
<li>登录<a href="https://connectus.garmin.cn/modern/courses"</summary>
</entry>
<entry>
<title>cesium学习笔记</title>
<link href="http://horsefaced.github.io/2018/04/16/cesium%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
<id>http://horsefaced.github.io/2018/04/16/cesium%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
<published>2018-04-16T09:01:44.000Z</published>
<updated>2018-07-04T22:12:51.488Z</updated>
<content type="html"><![CDATA[<h3 id="2018-05-28"><a href="#2018-05-28" class="headerlink" title="2018.05.28"></a>2018.05.28</h3><p>至少在Chrome浏览器中,Cesium 的 Double_Click 事件触发之前会触发两次 Click 事件,所以并不推荐使用 Double_Click 事件来处理操作</p><h3 id="2018-05-21"><a href="#2018-05-21" class="headerlink" title="2018.05.21"></a>2018.05.21</h3><p>Geometry 不是地形的一部分,但 3DTileset 的 model 是</p><p>Geometry的缺省boundingsphere 是整个地球</p><p>Viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin) 添加后会造成</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line">viewer.screenSpaceEventHandler.setInputAction(<span class="function">(<span class="params">event</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> picked = viewer.scene.pick(event.endPosition);</span><br><span class="line"> <span class="keyword">if</span> (picked !== prePicked) {</span><br><span class="line"> <span class="keyword">if</span> (prePicked) prePicked.color = preStyle;</span><br><span class="line"> <span class="keyword">if</span> (picked) {</span><br><span class="line"> prePicked = picked, preStyle = picked.color;</span><br><span class="line"> picked.color = Cesium.Color.YELLOW.withAlpha(<span class="number">0.5</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);</span><br></pre></td></tr></table></figure><p>这段代码出问题,它好像会改变 preStyle 的颜色</p><h3 id="2018-05-14"><a href="#2018-05-14" class="headerlink" title="2018.05.14"></a>2018.05.14</h3><p><a href="https://cesium.com/blog/2018/05/01/cesium-version-1.45-released/">https://cesium.com/blog/2018/05/01/cesium-version-1.45-released/</a></p><p>测试线段长度</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> hierarchy = entity.polyline.positions._value;</span><br><span class="line"> hierarchy = Cesium.PolylinePipeline.generateArc({</span><br><span class="line"> positions: hierarchy,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">let</span> vector = <span class="keyword">new</span> Cesium.Cartesian3();</span><br><span class="line"> <span class="keyword">let</span> distance = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">3</span>; i < hierarchy.length; i += <span class="number">3</span>) {</span><br><span class="line"> vector.x = hierarchy[i] - hierarchy[i - <span class="number">3</span>];</span><br><span class="line"> vector.y = hierarchy[i + <span class="number">1</span>] - hierarchy[i - <span class="number">2</span>];</span><br><span class="line"> vector.z = hierarchy[i + <span class="number">2</span>] - hierarchy[i - <span class="number">1</span>];</span><br><span class="line"> distance += Cesium.Cartesian3.magnitude(vector);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>测试polygon的面积</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Get the polygon from your "entity"</span></span><br><span class="line"><span class="keyword">var</span> polygon = theEntity.polygon;</span><br><span class="line"><span class="keyword">var</span> hierarchy = polygon.hierarchy._value;</span><br><span class="line"><span class="comment">// "indices" here defines an array, elements of which defines the indice of a vector</span></span><br><span class="line"><span class="comment">// defining one corner of a triangle. Add up the areas of those triangles to get</span></span><br><span class="line"><span class="comment">// an approximate area for the polygon</span></span><br><span class="line"><span class="keyword">var</span> indices = Cesium.PolygonPipeline.triangulate(hierarchy.positions, hierarchy.holes);</span><br><span class="line"><span class="keyword">var</span> area = <span class="number">0</span>; <span class="comment">// In square kilometers</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < indices.length; i += <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">var</span> vector1 = hierarchy.positions[indices[i]];</span><br><span class="line"> <span class="keyword">var</span> vector2 = hierarchy.positions[indices[i+<span class="number">1</span>]];</span><br><span class="line"> <span class="keyword">var</span> vector3 = hierarchy.positions[indices[i+<span class="number">2</span>]];</span><br><span class="line"> <span class="comment">// These vectors define the sides of a parallelogram (double the size of the triangle)</span></span><br><span class="line"> <span class="keyword">var</span> vectorC = Cesium.Cartesian3.subtract(vector2, vector1, <span class="keyword">new</span> Cesium.Cartesian3());</span><br><span class="line"> <span class="keyword">var</span> vectorD = Cesium.Cartesian3.subtract(vector3, vector1, <span class="keyword">new</span> Cesium.Cartesian3());</span><br><span class="line"> <span class="comment">// Area of parallelogram is the cross product of the vectors defining its sides</span></span><br><span class="line"> <span class="keyword">var</span> areaVector = Cesium.Cartesian3.cross(vectorC, vectorD, <span class="keyword">new</span> Cesium.Cartesian3());</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Area of the triangle is just half the area of the parallelogram, add it to the sum.</span></span><br><span class="line"> area += Cesium.Cartesian3.magnitude(areaVector)/<span class="number">2.0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>来自 <a href="https://groups.google.com/forum/#!topic/cesium-dev/EimmL-poCDI">https://groups.google.com/forum/#!topic/cesium-dev/EimmL-poCDI</a> </p><p>3DTileset不是一次性加入全部tile,所以要定位到某个tile,必须在外部保存tile的某个属性和它的位置地址,把视角平移过去后,在3DTileset加载完成后。才可以通过 3DTileset.tileVisible 找到有这个属性的 feature 然后着色,或者通过 Cesium3DTileStyle 着色。</p><p>根据建筑物坐标与高度,定位并调整视角</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> long = <span class="number">-1.2917727072831369</span>, lat = <span class="number">0.7105749513910979</span>, height = <span class="number">547.7591871983744</span>;</span><br><span class="line"><span class="keyword">let</span> heading = viewer.camera.heading, pitch = viewer.camera.pitch;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> position = viewer.scene.globe.ellipsoid.cartographicToCartesian(<span class="keyword">new</span> Cesium.Cartographic(long, lat, <span class="number">0.5</span> * height));</span><br><span class="line"><span class="keyword">let</span> offset = offsetFromHeadingPitchRange(heading, pitch, height * <span class="number">2.0</span>);</span><br><span class="line"><span class="keyword">let</span> transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);</span><br><span class="line">Cesium.Matrix4.multiplyByPoint(transform, offset, position);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">offsetFromHeadingPitchRange</span>(<span class="params">heading, pitch, range</span>) </span>{</span><br><span class="line"> pitch = Cesium.Math.clamp(pitch, -Cesium.Math.PI_OVER_TWO, Cesium.Math.PI_OVER_TWO);</span><br><span class="line"> heading = Cesium.Math.zeroToTwoPi(heading) - Cesium.Math.PI_OVER_TWO;</span><br><span class="line"> <span class="keyword">var</span> pitchQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Y, -pitch);</span><br><span class="line"> <span class="keyword">var</span> headingQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Z, -heading);</span><br><span class="line"> <span class="keyword">var</span> rotQuat = Cesium.Quaternion.multiply(headingQuat, pitchQuat, headingQuat);</span><br><span class="line"> <span class="keyword">var</span> rotMatrix = Cesium.Matrix3.fromQuaternion(rotQuat);</span><br><span class="line"> <span class="keyword">var</span> offset = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_X);</span><br><span class="line"> Cesium.Matrix3.multiplyByVector(rotMatrix, offset, offset);</span><br><span class="line"> Cesium.Cartesian3.negate(offset, offset);</span><br><span class="line"> Cesium.Cartesian3.multiplyByScalar(offset, range, offset);</span><br><span class="line"> <span class="keyword">return</span> offset;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>例子:<br><a href="http://www.virtualcitysystems.de/en/">http://www.virtualcitysystems.de/en/</a></p><p><a href="http://nrw.virtualcitymap.de/#/">http://nrw.virtualcitymap.de/#/</a></p><p><a href="http://www.3dcitydb.net/3dcitydb/fileadmin/3DWebClient/index.html">http://www.3dcitydb.net/3dcitydb/fileadmin/3DWebClient/index.html</a></p><p><a href="https://cesiumjs.org/demos/">https://cesiumjs.org/demos/</a></p><p><a href="http://cybercity3d.s3-website-us-east-1.amazonaws.com/?city=WashingtonDC">http://cybercity3d.s3-website-us-east-1.amazonaws.com/?city=WashingtonDC</a></p><h3 id="2018-05-08"><a href="#2018-05-08" class="headerlink" title="2018.05.08"></a>2018.05.08</h3><p>设置Home的位置</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Cesium.Camera.DEFAULT_VIEW_FACTOR = <span class="number">-0.3</span>;</span><br><span class="line">Cesium.Camera.DEFAULT_VIEW_RECTANGLE = tileset._root._boundingVolume.rectangle;</span><br></pre></td></tr></table></figure><p>Camera.lookAt 会锁住 camera 的视点,让鼠标不能移动 camera,需要 viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)</p><p>距离 3DTileset 1.5倍 boundingSpere.radius,<br>高度为 1.5 * distance / Math.tan(Cesium.Math.toRadians(70.0)),<br>heading: Cesium.Math.toRadians(0.0),<br>pitch: Cesium.Math.toRadians(-20.0),<br>roll: 0.0,<br>时候,角度比较好</p><p>通过 Camera.pickEllipsoid 得到的坐标是 WGS84 坐标系下的,在有地形信息的情况下就会偏移,就算通过 sampleTerrianMostDetailed 转化过也一样</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> coordinate = viewer.camera.pickEllipsoid(event.position, viewer.scene.globe.ellipsoid);</span><br><span class="line">coordinate = Cesium.Cartographic.fromCartesian(coordinate);</span><br><span class="line">Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [coordinate]).then(<span class="function">(<span class="params">samples</span>) =></span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> sample <span class="keyword">of</span> samples) sample.height += <span class="number">10.0</span>;</span><br><span class="line"> viewer.entities.add({</span><br><span class="line"> position: Cesium.Cartographic.toCartesian(samples[<span class="number">0</span>], viewer.scene.globe.ellipsoid),</span><br><span class="line"> point: {</span><br><span class="line"> pixelSize: <span class="number">5</span>,</span><br><span class="line"> color: Cesium.Color.GREEN,</span><br><span class="line"> heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,</span><br><span class="line"> },</span><br><span class="line"> });</span><br><span class="line"> });</span><br></pre></td></tr></table></figure><p>通过下面这个 Ray 相交得到的坐标是可以有地形高度的,取决于得到是有没有加载地形信息,如果有则有高度信息,没有就没有</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> ray = viewer.camera.getPickRay(event.position);</span><br><span class="line"> <span class="keyword">let</span> positon = viewer.scene.globe.pick(ray, viewer.scene);</span><br><span class="line"> viewer.entities.add({</span><br><span class="line"> position: positon,</span><br><span class="line"> point: {</span><br><span class="line"> pixelSize: <span class="number">5</span>,</span><br><span class="line"> color: Cesium.Color.LIME, </span><br><span class="line"> heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, </span><br><span class="line"> },</span><br><span class="line"> });</span><br></pre></td></tr></table></figure><p>screenSpaceEventHandler.setInputAction 居然一个类型只可以有一个事件监听</p><p>得到两点方向的代码</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Add button to View Aircraft at a Fixed Angle relative to aircraft</span></span><br><span class="line">Sandcastle.addToolbarButton(<span class="string">'View Aircraft Fixed Angle'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> viewer.trackedEntity = <span class="literal">undefined</span>;</span><br><span class="line"> viewer.clock.onTick.addEventListener(<span class="function"><span class="keyword">function</span> (<span class="params">clock</span>) </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//get 2 positions close together timewise</span></span><br><span class="line"> <span class="keyword">var</span> CC3 = Cesium.Cartesian3;</span><br><span class="line"> <span class="keyword">var</span> position1 = entity.position.getValue(clock.currentTime, <span class="keyword">new</span> CC3());</span><br><span class="line"> <span class="keyword">var</span> position2 = entity.position.getValue(Cesium.JulianDate.addSeconds(clock.currentTime, <span class="number">1</span> / <span class="number">60</span>, <span class="keyword">new</span> Cesium.JulianDate()), <span class="keyword">new</span> CC3());</span><br><span class="line"></span><br><span class="line"> <span class="comment">//velocity in terms of Earth Fixed </span></span><br><span class="line"> <span class="keyword">var</span> Wvelocity = CC3.subtract(position2, position1, <span class="keyword">new</span> CC3());</span><br><span class="line"> CC3.normalize(Wvelocity, Wvelocity);</span><br><span class="line"> <span class="keyword">var</span> Wup = <span class="keyword">new</span> CC3(); <span class="keyword">var</span> Weast = <span class="keyword">new</span> CC3(); <span class="keyword">var</span> Wnorth = <span class="keyword">new</span> CC3();</span><br><span class="line"> Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(position1, Wup);</span><br><span class="line"> CC3.cross({ <span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span>, <span class="attr">z</span>: <span class="number">1</span> }, Wup, Weast);</span><br><span class="line"> CC3.cross(Wup, Weast, Wnorth);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//velocity in terms of local ENU</span></span><br><span class="line"> <span class="keyword">var</span> Lvelocity = <span class="keyword">new</span> CC3();</span><br><span class="line"> Lvelocity.x = CC3.dot(Wvelocity, Weast);</span><br><span class="line"> Lvelocity.y = CC3.dot(Wvelocity, Wnorth);</span><br><span class="line"> Lvelocity.z = CC3.dot(Wvelocity, Wup);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//angle of travel</span></span><br><span class="line"> <span class="keyword">var</span> Lup = <span class="keyword">new</span> CC3(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>); <span class="keyword">var</span> Least = <span class="keyword">new</span> CC3(<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>); <span class="keyword">var</span> Lnorth = <span class="keyword">new</span> CC3(<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">var</span> x = CC3.dot(Lvelocity, Least);</span><br><span class="line"> <span class="keyword">var</span> y = CC3.dot(Lvelocity, Lnorth);</span><br><span class="line"> <span class="keyword">var</span> z = CC3.dot(Lvelocity, Lup);</span><br><span class="line"> <span class="keyword">var</span> angle = <span class="built_in">Math</span>.atan2(x, y);<span class="comment">//math: y b4 x, heading: x b4 y</span></span><br><span class="line"> <span class="keyword">var</span> pitch = <span class="built_in">Math</span>.asin(z);<span class="comment">//make sure Lvelocity is unitized</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//angles offsets</span></span><br><span class="line"> angle += <span class="number">0</span> / <span class="number">180</span> * <span class="built_in">Math</span>.PI;</span><br><span class="line"> pitch += <span class="number">-20</span> / <span class="number">180</span> * <span class="built_in">Math</span>.PI;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> range = <span class="number">80</span>;</span><br><span class="line"> <span class="keyword">var</span> offset = <span class="keyword">new</span> Cesium.HeadingPitchRange(angle, pitch, range);</span><br><span class="line"> viewer.scene.camera.lookAt(entity.position.getValue(clock.currentTime), offset);</span><br><span class="line"> }); <span class="comment">//end event listener</span></span><br><span class="line">}); <span class="comment">//end button</span></span><br></pre></td></tr></table></figure><p>orientation : new Cesium.VelocityOrientationProperty(position) 可以得到某点的方向,但 position 需要是 Cesium.SampledProperty 根据时间的采样属性</p><h3 id="2018-05-07"><a href="#2018-05-07" class="headerlink" title="2018.05.07"></a>2018.05.07</h3><p>Globe 上的 ClippingPlane 定一个法向量就可以了,distance是相对与 ClippingPlaneCollection 的 ModelMatrix 的,它是以笛卡尔坐标系的中心建立了一个穿过整个坐标系的平面组,所以会在地球表面形成两个相对的切面。</p><p>如何计算一个离某个点距离为 radius 的圆的坐标:</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> n = <span class="number">180</span>, points = [];</span><br><span class="line"><span class="keyword">let</span> center = tileset.boundingSphere.center, radius = tileset.boundingSphere.radius;</span><br><span class="line"><span class="keyword">let</span> R = viewer.scene.globe.ellipsoid.maximumRadius; <span class="comment">//地球半径</span></span><br><span class="line">center = Cesium.Cartographic.fromCartesian(center); <span class="comment">//转成经纬度坐标</span></span><br><span class="line"><span class="comment">//latB = latA-((radius * Math.sin(Math.PI * i / n)) * 180) / (Math.PI * ellipsoid.R)</span></span><br><span class="line"><span class="comment">//latB = LatA-(Y*180)/(Math.PI * e)</span></span><br><span class="line"><span class="comment">//loB = loA - ((rdius * Math.cost(Math.PI * i / n)) * 180) / (Math.PI * ellipsoid.R * Math.cos((LatA + LatB) / 2))</span></span><br><span class="line"><span class="comment">//loB = loA - (x*180)/(math.pi * r * cost((latA + latB) / 2))</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = -n; i <= n; i++) {</span><br><span class="line"> <span class="keyword">let</span> x = <span class="built_in">Math</span>.cos(<span class="built_in">Math</span>.PI * i / n) * radius;</span><br><span class="line"> <span class="keyword">let</span> y = <span class="built_in">Math</span>.sin(<span class="built_in">Math</span>.PI * i / n) * radius;</span><br><span class="line"> <span class="comment">//let LatB = center.latitude - (y * 180) / (Math.PI * R);</span></span><br><span class="line"> <span class="keyword">let</span> LatB = center.latitude - (y / R);</span><br><span class="line"> <span class="comment">//let LonB = center.longitude - (x * 180) / (Math.PI * R * Math.cos((center.latitude + LatB) / 2));</span></span><br><span class="line"> <span class="keyword">let</span> LonB = center.longitude - (x / (R * <span class="built_in">Math</span>.cos((center.latitude + LatB) / <span class="number">2</span>)));</span><br><span class="line"> <span class="keyword">let</span> point = <span class="keyword">new</span> Cesium.Cartographic(LonB, LatB);</span><br><span class="line"> point = viewer.scene.globe.ellipsoid.cartographicToCartesian(point);</span><br><span class="line"> points.push(point);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个是不精确的版本,在小范围误差可接受,参考自<a href="https://tech.meituan.com/lucene-distance.html">地理空间距离计算优化</a></p><p>Polyline 这个 Geometry 不支持 heightReference,于是不会自动贴地。必须通过 Cesium.sampleTerrainMostDetailed 这个方法吧 positions 中的点进行高度调整,其中 pointsR 为高度为零的 Cartographic 对象。</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line">Cesium.when(Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, pointsR), <span class="function">(<span class="params">samples</span>) =></span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> sample <span class="keyword">of</span> samples) sample.height += <span class="number">10.0</span>;</span><br><span class="line"> viewer.entities.add({</span><br><span class="line"> polyline: {</span><br><span class="line"> positions: Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(samples),</span><br><span class="line"> width: <span class="number">5</span>,</span><br><span class="line"> material: Cesium.Color.WHITE,</span><br><span class="line"> },</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h3 id="2018-05-03"><a href="#2018-05-03" class="headerlink" title="2018.05.03"></a>2018.05.03</h3><p>不支持 KHR_materials_pbrSpecularGlossiness </p><p>Entity 保存了 Graphic<br>Graphic 保存 Geometry 对象的描述属性,但没有定义怎么画出这个对象</p><p>Billboard:</p><p>Billboard</p><ul><li>@performance Reading a property, e.g., {@link Billboard#show}, is constant time.</li><li>Assigning to a property is constant time but results in</li><li>CPU to GPU traffic when {@link BillboardCollection#update} is called. The per-billboard traffic is</li><li>the same regardless of how many properties were updated. If most billboards in a collection need to be</li><li>updated, it may be more efficient to clear the collection with {@link BillboardCollection#removeAll}</li><li>and add new billboards instead of modifying each one.<br>照这么说来,当 BillboardCollection.update 的时候才会处理成 GPU 的数据发送至 GPU</li></ul><p>BillboradGraphics<br>BillboardGraphicsSpec</p><p>BillboardCollection<br>BillboardCollectionSpec</p><p>BillboardVisualizer<br>BillboardVisualizerSpec</p><p>BillboardCollectionFS.glsl<br>BillboardCollectionVS.glsl</p><p>createBillboardPointCallback </p><p>Entity 不指定方向的情况下用 eastNorthUpToFixedFrame</p><h3 id="2018-04-27"><a href="#2018-04-27" class="headerlink" title="2018.04.27"></a>2018.04.27</h3><p>Cesium 画geometry的效率<br>Billboard 》polyline > box = plane 》 polygon(不带挤压高度) > polygon(带挤压高度) </p><p>一万个 ellipse ellipsoid cylinder corrdior 会有内存不足的问题</p><p>画一个带指示线的标签</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> viewer = <span class="keyword">new</span> Cesium.Viewer(<span class="string">'cesiumContainer'</span>,</span><br><span class="line"> {<span class="attr">timeline</span>: <span class="literal">false</span>, <span class="attr">animation</span>: <span class="literal">false</span>});</span><br><span class="line"><span class="keyword">var</span> scene = viewer.scene;</span><br><span class="line"><span class="keyword">var</span> offsetX = <span class="number">50</span>, offsetY = <span class="number">-80</span>;</span><br><span class="line"><span class="keyword">var</span> pos = Cesium.Cartesian3.fromDegrees(<span class="number">-75.1641667</span>, <span class="number">29.9522222</span>);</span><br><span class="line"><span class="keyword">var</span> labels = scene.primitives.add(<span class="keyword">new</span> Cesium.LabelCollection());</span><br><span class="line">labels.add({</span><br><span class="line"> position: pos,</span><br><span class="line"> text: <span class="string">'Another label'</span>,</span><br><span class="line"> pixelOffset: <span class="keyword">new</span> Cesium.Cartesian2(offsetX, offsetY)</span><br><span class="line">});</span><br><span class="line"><span class="keyword">var</span> canvas = <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>);</span><br><span class="line">canvas.width = <span class="built_in">Math</span>.abs(offsetX);</span><br><span class="line">canvas.height = <span class="built_in">Math</span>.abs(offsetY);</span><br><span class="line"><span class="keyword">var</span> context2D = canvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line">context2D.beginPath();</span><br><span class="line">context2D.lineWidth = <span class="number">3</span>;</span><br><span class="line">context2D.strokeStyle = <span class="string">'#ffffff'</span>;</span><br><span class="line">context2D.moveTo((offsetX < <span class="number">0</span>) ? -offsetX : <span class="number">0</span>, (offsetY < <span class="number">0</span>) ? -offsetY : <span class="number">0</span>);</span><br><span class="line">context2D.lineTo((offsetX < <span class="number">0</span>) ? <span class="number">0</span> : offsetX, (offsetY < <span class="number">0</span>) ? <span class="number">0</span> : offsetY);</span><br><span class="line">context2D.stroke();</span><br><span class="line"><span class="keyword">var</span> billboards = scene.primitives.add(<span class="keyword">new</span> Cesium.BillboardCollection());</span><br><span class="line"><span class="keyword">var</span> billboard = billboards.add({</span><br><span class="line"> color : Cesium.Color.RED,</span><br><span class="line"> image : canvas,</span><br><span class="line"> pixelOffset: <span class="keyword">new</span> Cesium.Cartesian2(offsetX * <span class="number">0.5</span>, offsetY * <span class="number">0.5</span>),</span><br><span class="line"> position : pos</span><br><span class="line">});</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><a href="https://stackoverflow.com/questions/32716118/cesium-js-how-draw-line-binding-a-label-to-a-position">https://stackoverflow.com/questions/32716118/cesium-js-how-draw-line-binding-a-label-to-a-position</a></p><h3 id="2018-04-26"><a href="#2018-04-26" class="headerlink" title="2018.04.26"></a>2018.04.26</h3><p>Primitive 对象有定点着色器与片元着色器的源代码入口</p><p>Viewer.scene.drillPick 返回的 pickedObjects 数组对象<br>pickedObject.id 为 entity<br>pickedObject.primitive 为相应的 primitive 对象</p><p>添加X,Y,Z三轴线</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(position, hprRollZero, Cesium.Ellipsoid.WGS84, converter);</span><br><span class="line"> scene.primitives.add(<span class="keyword">new</span> Cesium.DebugModelMatrixPrimitive({</span><br><span class="line"> modelMatrix : modelMatrix,</span><br><span class="line"> length : <span class="number">300.0</span>,</span><br><span class="line"> width : <span class="number">10.0</span></span><br><span class="line"> }));</span><br></pre></td></tr></table></figure><h3 id="2018-04-24"><a href="#2018-04-24" class="headerlink" title="2018.04.24"></a>2018.04.24</h3><p>Geometry画出来的图形是以地球坐标系来画的, Model是以模型自身的坐标系来画的,3DTileset是以地球坐标系来画的</p><p>3DTileset.debugWireframe可以显示骨架</p><h3 id="2018-04-16"><a href="#2018-04-16" class="headerlink" title="2018.04.16"></a>2018.04.16</h3><p>Cesium Pick有BUG,它是根据3DTiles的boundingSphere加上geometryError来确定模型占地面积,由maximumHeight的两倍来确定模型的高度。模型在这个范围内浮动,不会有明显的问题,超出这个范围就会有点选不中,显示异常的问题。</p><p>3DTileset的modelMatrix是整个set共用的,pickedFeature里的</p>]]></content>
<summary type="html"><h3 id="2018-05-28"><a href="#2018-05-28" class="headerlink" title="2018.05.28"></a>2018.05.28</h3><p>至少在Chrome浏览器中,Cesium 的 Double_Click 事</summary>
<category term="cesium" scheme="http://horsefaced.github.io/tags/cesium/"/>
</entry>
<entry>
<title>porlex mini 调豆子</title>
<link href="http://horsefaced.github.io/2018/03/06/porlex-mini-%E8%B0%83%E8%B1%86%E5%AD%90/"/>
<id>http://horsefaced.github.io/2018/03/06/porlex-mini-%E8%B0%83%E8%B1%86%E5%AD%90/</id>
<published>2018-03-05T23:43:05.000Z</published>
<updated>2018-03-05T23:53:04.594Z</updated>
<content type="html"><![CDATA[<p><img src="/images/cafe/porlexMiniAdjust.jpg" alt="题图"></p><ul><li>两格:均匀度较好,粉细,用于爱乐压则下压压力比较大</li><li>三、四格:均匀度尚可,手冲与爱乐压时水温要控制,不要太高</li><li>五、六格:均匀度最差,不推荐用这个刻度</li><li>七格:均匀度相对五有提升,颗粒合适法压,如果用于手冲,水温可高,合适户外、旅行等对温度比较不好控制的场合</li><li>十格:颗粒过大,没可用性</li></ul>]]></content>
<summary type="html"><p><img src="/images/cafe/porlexMiniAdjust.jpg" alt="题图"></p>
<ul>
<li>两格:均匀度较好,粉细,用于爱乐压则下压压力比较大</li>
<li>三、四格:均匀度尚可,手冲与爱乐压时水温要控制,不要太高</li>
</summary>
<category term="咖啡" scheme="http://horsefaced.github.io/categories/%E5%92%96%E5%95%A1/"/>
</entry>
<entry>
<title>鱼家之哥斯达黎加赛罗庄园黄蜜</title>
<link href="http://horsefaced.github.io/2018/03/03/%E9%B1%BC%E5%AE%B6%E4%B9%8B%E5%93%A5%E6%96%AF%E8%BE%BE%E9%BB%8E%E5%8A%A0%E8%B5%9B%E7%BD%97%E5%BA%84%E5%9B%AD%E9%BB%84%E8%9C%9C/"/>
<id>http://horsefaced.github.io/2018/03/03/%E9%B1%BC%E5%AE%B6%E4%B9%8B%E5%93%A5%E6%96%AF%E8%BE%BE%E9%BB%8E%E5%8A%A0%E8%B5%9B%E7%BD%97%E5%BA%84%E5%9B%AD%E9%BB%84%E8%9C%9C/</id>
<published>2018-03-02T23:28:14.000Z</published>
<updated>2018-03-06T00:43:36.248Z</updated>
<content type="html"><![CDATA[<h2 id="鱼家的哥斯达黎加赛罗庄园黄蜜"><a href="#鱼家的哥斯达黎加赛罗庄园黄蜜" class="headerlink" title="鱼家的哥斯达黎加赛罗庄园黄蜜"></a>鱼家的哥斯达黎加赛罗庄园黄蜜</h2><p><img src="/images/cafe/20180303%E9%B1%BC%E5%AE%B6%E5%93%A5%E6%96%AF%E8%BE%BE%E9%BB%8E%E5%8A%A0%E8%B5%9B%E7%BD%97%E5%BA%84%E5%9B%AD%E9%BB%84%E8%9C%9C.jpg" alt="题图"></p><blockquote><p>产地:哥斯达黎加</p></blockquote>]]></content>
<summary type="html"><h2 id="鱼家的哥斯达黎加赛罗庄园黄蜜"><a href="#鱼家的哥斯达黎加赛罗庄园黄蜜" class="headerlink" title="鱼家的哥斯达黎加赛罗庄园黄蜜"></a>鱼家的哥斯达黎加赛罗庄园黄蜜</h2><p><img src="/images/cafe</summary>
<category term="咖啡" scheme="http://horsefaced.github.io/categories/%E5%92%96%E5%95%A1/"/>
</entry>
<entry>
<title>哥斯达黎加黑蜜</title>
<link href="http://horsefaced.github.io/2017/11/09/%E5%93%A5%E6%96%AF%E8%BE%BE%E9%BB%8E%E5%8A%A0%E9%BB%91%E8%9C%9C/"/>
<id>http://horsefaced.github.io/2017/11/09/%E5%93%A5%E6%96%AF%E8%BE%BE%E9%BB%8E%E5%8A%A0%E9%BB%91%E8%9C%9C/</id>
<published>2017-11-09T00:17:10.000Z</published>
<updated>2018-02-27T09:18:01.173Z</updated>
<content type="html"><![CDATA[<blockquote><p><img src="/images/cafe/20171104.JPG"><br>哥斯达黎加 塔拉珠 Canet Community<br>卡杜艾, 卡杜拉种<br>海拔 1700-1800<br>黑蜜处理</p></blockquote><a id="more"></a><p>这个豆子来自Canet Commuity, 这是一个由46家农庄组成的小社区. 有意思的是, 这里主要种植的是百香果, 咖啡算是副业了, 每年的产量都不大. 正因为如此, 也就可以采取特别严格的标准来采收选择豆子.</p><p>这豆子刚冲出来时, 并没有黑蜜处理那种酒味, 反而有着一种奶油瓜子的味道. 入口时, 伴随着奶油瓜子的浓香, 坚果的味道在口中回荡. 慢慢的, 一种甘草的苦味回上来, 让人感到仿佛在喝甘草茶.</p><p>时间流动, 温度下降, 奶油瓜子渐渐隐到背景之中. 甘草,柚子的味道占据了主频道,回味中醇香型白酒的味道在喉咙深处隐隐游荡.</p><p>温度进一步的下降,一种巧克力味道慢慢涌了上来,与仙草冻一起,结束了这美妙的味觉协奏曲.</p><p>这款黑蜜处理的咖啡豆在各个温度阶段都有着明确的主调,但之前表现过的调性也没有丢失,而是形成的背景,与当前的主调性完美的组合在一起,真不愧是以"巴赫"为名.</p><p><img src="/images/cafe/%E5%A4%8F%E4%BC%8D.png"></p><p>夏伍算是一家非常小的店, 就一个小店面, 背后看着都是一家人. 桌子就三张, 生意不错, 想来手冲之外的东西也是挺好的.<br>有一段时间没有去了,春节去的时候发现居然拆迁了,据说是要搬到边上的万科广场去。</p>]]></content>
<summary type="html"><blockquote>
<p><img src="/images/cafe/20171104.JPG"><br>哥斯达黎加 塔拉珠 Canet Community<br>卡杜艾, 卡杜拉种<br>海拔 1700-1800<br>黑蜜处理</p>
</blockquote></summary>
<category term="咖啡" scheme="http://horsefaced.github.io/categories/%E5%92%96%E5%95%A1/"/>
</entry>
<entry>
<title>贡嘎装备</title>
<link href="http://horsefaced.github.io/2017/10/12/%E8%B4%A1%E5%98%8E%E8%A3%85%E5%A4%87/"/>
<id>http://horsefaced.github.io/2017/10/12/%E8%B4%A1%E5%98%8E%E8%A3%85%E5%A4%87/</id>
<published>2017-10-11T22:42:28.000Z</published>
<updated>2017-10-14T01:32:34.000Z</updated>
<content type="html"><![CDATA[<p>这次贡嘎之行, 因某些意外加上老天配合, 于是晴天, 下雨, 冰雹, 下雪再加雨中夜路全都遇到了, 倒也是对装备全面的检测, 对我这个装备党来说, 苦逼是苦逼了一些, 但能看到自己找来的装备的好坏倒也是一种奇葩的乐趣.</p><p><img src="/images/gonga/%E8%90%A5%E5%9C%B0%E6%99%92%E8%A3%85%E5%A4%87.JPG" alt="苦逼营地晒装备"></p><a id="more"></a><h2 id="从衣服说起吧"><a href="#从衣服说起吧" class="headerlink" title="从衣服说起吧"></a>从衣服说起吧</h2><blockquote><p><img src="/images/gonga/%E7%BA%A2%E9%B8%9F%E4%B8%8E%E7%99%BD%E8%A3%99.jpeg" alt="裙子与冲锋衣"></p><p>最爱那一抹白色裙子上的蚊子血</p></blockquote><h3 id="冲锋衣"><a href="#冲锋衣" class="headerlink" title="冲锋衣"></a>冲锋衣</h3><blockquote><p><img src="/images/gonga/%E9%B8%9F%E5%86%B2.jpg" alt="鸟家冲锋衣"></p><p>Arc’teryx Alpha SL Hybrid Jacket-Men’s</p></blockquote><p>就在题图野外大晒衣的前一天晚上, 用这件衣服顶着冰雹, 小雪, 小雨从下午四点走到晚上八点半才到营地, 也能保的上半身干爽. 于是那些说 Gore-Tex® PacLite®的观点, 其实我是不信的.</p><p>结论: 重量340克左右挺好的, 暂没有升级的想法与必要.</p><h3 id="裙子"><a href="#裙子" class="headerlink" title="裙子"></a>裙子</h3><blockquote><p><img src="/images/gonga/%E8%A3%99%E5%AD%90.JPG" alt="伪娘范"></p><p>三峰家的白裙子</p></blockquote><p>自从迪姐的<del>我的户外伪娘生涯</del><a href="http://bbs.8264.com/forum-viewthread-tid-2026879-page-1-authorid-92276.html">裙角飞扬:一个人的格聂,非典型轻量化装备!</a>,之后就对如何在户外<del>装伪娘</del>轻量化的下半身防雨有了新期待.于是出发前搞了三峰的涂硅裙子决定与<del>贡嘎拼了</del>速干裤配合, 要不然在福州穿着冲锋裤出发, 那酸爽难以想像. 或者是单独带个速干裤, 也是我这种<del>可以好几天不洗澡</del>玩轻量化的人所不喜欢的多余重量.</p><p>其实出发前我对这个裙子是有些不屑的, 这种涂硅裙子轻飘飘的没什么质感, 风一吹就能装梦露, 而且还不是狗踢死.</p><p><img src="/images/gonga/%E6%A2%A6%E9%9C%B2.jpeg" alt="来一个梦露"></p><p><strong>毛主席教导我们: 装备还是用着好!</strong></p><p>于是还是在题图野外大晒衣的前一天晚上, 这条涂硅裙子居然真的保住了我下<del>体</del>半身的干爽, 当然我的鞋是湿的, 这是后话. 这么薄的涂硅面料在一堆的矮灌木中走过, 居然<del>不带走一丝云彩</del>没有被刮破, 也真是利害. 不过由于本人的<del>投降主义思想</del>懒, 在爬日乌且垭口的小雨中没有穿上这条裙子, 所以在垭口爬升的大风中表现如何还有待测试.</p><p>结论: 在没什么风的情况下, 这条裙子个人感觉可以完全替换冲锋裤, 与速干配合, 适合更多场景.</p><h3 id="神衣"><a href="#神衣" class="headerlink" title="神衣"></a>神衣</h3><blockquote><p><img src="/images/gonga/%E7%A5%9E%E8%A1%A3.JPG" alt="神衣"></p><p>混8264的一看标题就知道是什么东西.</p></blockquote><p>以前我有一个不带帽子的, 今年托海淘大神给搞了个带帽子的. 这次算是处女穿, 发现带个帽子还真是好啊! 不过带上帽子还真是丑啊! 当然人丑就要<del>多读书</del>少拍照, 于是就没找到带上帽子的丑照. 它的帽子里面是没有抓绒材料的, 想来就是一个用途, 扛风.<br>我带的是一个buff的帽子, 这帽子轻, 好收纳, 超透气, 于是完全的不防风. 高原很多的时候明明不是很冷但就是风太大, 吹的头痛, 加上神衣的帽子后, 那感觉风说不上停止了, 但也确实是小了非常多. 于是头也不痛了<del>腰也不酸了,走路也有劲了, 一气上五楼</del>. 不过说来因为把脖子也挡住了, 温标也提升了不少, 爬升的时候脖子还是会感觉有点过热, 可能这时候有个windstopper的帽子, 然后把脖子放出来凉快的解决方案会更好.</p><p>结论: 其实我一向不喜欢软壳, 太重收纳体积太大, 人肥的话(对,就是说我)还会过保温. 抓绒又完全的不防风, 有点小风那保温基本为零. 这些年玩户外, 行进间基本就是神衣, 加一件冲锋衣可以当中间层, 更多的时候就直接是最外层, 又防风又有一定的保温性能, 透气还特别的好, 绝对是行进间好衣服.</p><h3 id="羽绒套"><a href="#羽绒套" class="headerlink" title="羽绒套"></a>羽绒套</h3><blockquote><p><img src="/images/gonga/%E6%8A%96%E5%B8%90%E7%AF%B7.GIF" alt="抖帐篷"></p><p>那翩翩的中年走着魔性的脚步</p></blockquote><p>说来对于上班的苦逼来说, 也就只有国庆能走个长线<del>请还我们五一</del>. 这个时候, 西北西南这些个线都开始冷了, 于是羽绒服羽绒裤羽绒脚套的组合算是固定的了. 营地穿能极大的提升幸福感<del>另一个能极大提升幸福感的是拖鞋</del>配合睡袋还能极大的提升温标.</p><p>说来这件羽绒服与羽绒马甲与羽绒睡袋都已经八年了, 这算是华巍最早期的产品了. 当时咬牙买这些东西的场景仿佛还是昨天, 如今面对这些依旧好用的羽绒制品, 当初花的钱其实还是值得的, 我想最好的评价也就是如此了吧. 一件装备用着用着成了最顺手的老伙计, 岁月让一切都那么的自然, 自然的无话可说.</p><h2 id="背包"><a href="#背包" class="headerlink" title="背包"></a>背包</h2><blockquote><p><img src="/images/gonga/%E4%B8%89%E6%98%8E%E6%B2%BB.png" alt="酱铺的三明治"></p><p>给点饭可好</p></blockquote><p>这次的三明治算是新包, 要是不背它的话, 我就背上花岗岩烈火了. 烈火说来也是五年的包了, 每次要出掉时都舍不得然后放买家鸽子.<br><strong>白岩松说过: 背包还是防水的好!</strong><br>白老板真是英明, 我的这个三明治是用x-pac材料的, 防水背包的最大好处是自身防水外还不吸水, 不会有普通背包越淋越重的问题, 非常合适我这种老弱病残. 在第二天夜路加各种雨雪的情况下, 背包里没有单独防水设施的睡袋衣服都保持了干爽.另一个好处是因为包体防水, 里面就不用做防水措施了, 于是羽绒服羽绒裤羽绒脚套各种衣服就可以一统乱塞, 把包能撑的满满的, 包中无效体积会少很多.<br>三明治带了一个约二十升的网兜, 算是UL包中比较大的那种了, 挺能装的. 我塞进了帐篷, 地布, 地钉, 保温瓶, 酒精棉球, 裙子, 空水袋还有一把伞. 听上去没多少, 其实主要是我的1.0厚度的粗苯帐篷折叠体积实在是太大了, 折叠起来要比同大小的20D双硅帐篷大了一半这样子, 当初也是怕了才搞了这么厚的, 现在想想也是过头了吧? 本来酱铺推荐是网兜要用两边的弹力绳固定起来, 就是图中我用来绑可乐的弹力绳. 但, 酱桑, 你给的弹力绳太短啦! 我放了这么多东西后, 那绳子都被我拉到最大快断了也绑不上去, 只好这么自由自在由它去了.<br>三明治包的本体有四十五升的容量. 就其设计风格来说属于瘦高型的, 就是直径比较小, 靠包体高度的提升来调整容量. 于是就出现了提升到极限时包会过高, 抬头时会碰到包体的尴尬. 希望酱桑以后设计时, 能适当放宽包体, 减小包的高度. HMG的包在包口设计上加了一个魔术贴, 这个小设计粗看起来没什么用处, 在实际使用中就会发现, 一方面你能很方便的把包口合成一个长条然后开始圈, 另一方面当你把包升到最大时这个魔术贴还能保证你把包口给关起来. 而且HMG的包口可以向包两边封闭, 当装到最大只能合上包口不能多折几次的时候就非常用有了. 而三明治的包上面就没有这些设计, 在包打到最高的时候, 会尴尬的发现想要封起包口是个麻烦事情, 必需尽可能的把包内物质再压一压才能照它设计的做法折上三次左右再扣起来. 如果压的不够或者不能压更多的时候, 那个包口又没有魔术贴可以自封, 在折一次的必然会开口, 在折两次时, 还是有开口的可能性. 这么说可能不直观, 大家可以找一下防水袋子自己试一下, 装到尽可能满, 然后你就明白了. 推荐酱桑能在包口上增加魔术贴封口, 另加两个小扣件可以绑在包边上的压缩绳上, 使得包口可以向两边封闭.<br><img src="/images/gonga/%E5%8C%85%E8%85%B0%E5%B8%A6.JPG" alt="包的腰带"><br>这个包的腰带与背板连接是靠两边的织带. 这种连接设计就会造成一个很明显的问题, 包的整个重量都会放在了两边的织带上, 于是织带的磨损不可避免. 图上这个情况是这个包第一次走线完的情况, 耐久性如何有待进一步的使用后才能知道. 推荐酱桑更改设计, 扩大腰带与背板的连接点数量, 使重量能分布到更多的地方去.<br><img src="/images/gonga/%E5%8C%85%E8%83%8C%E6%9D%BF.JPG" alt="背板"><br>这包最好的东西个人认为就是那块背板了, 在适当的硬度的同时, 又有很好的韧度. 这种外架包最大的问题如<a href="https://mp.weixin.qq.com/s/89dOHpQ5ldZYzVqgMZAQCQ">那些死贵的户外背包,都是啥?</a>这个公众号文章里说的那样子, 外架包因为框架与包体分开会造成行动时包体不稳. 这个问题在这块背板上不存在. 鱼骨型的设计加上碳纤维的韧度, 在用力把两边收缩绳都拉紧, 让背板的鱼骨边有一定的弯曲对包体形成包围后, 三明治的包体与背板仿佛内架包一样能结合的非常紧密, 完全没有没拉紧时晃动不稳的情况, 这时背包有如一体<del>行动如风</del>. 它后面有六块泡沫垫的通风设计, 看着挺简单的, 使用起来确实有用, 在贡嘎时还感觉通风过了头挺冷得.<br><img src="/images/gonga/%E8%85%B0%E5%B8%A6%E5%8C%85.JPG" alt="腰带包"><br>这两腰带包大小挺合适的还防水, 手机放里面下雨也都没事, 不过推荐酱桑把拉链设计成双向的, 这种左右不一样方向在使用上会怪怪的不对称的感觉.</p><h2 id="杂项"><a href="#杂项" class="headerlink" title="杂项"></a>杂项</h2><h3 id="MSR-Trailshot-Microfilter"><a href="#MSR-Trailshot-Microfilter" class="headerlink" title="MSR Trailshot Microfilter"></a>MSR Trailshot Microfilter</h3><blockquote><p><img src="/images/gonga/msr.png" alt="净水器"></p><p>壮士, 干了这碗牛粪水! 来世还生种花家!</p></blockquote><p>这是我用过最好用的净水器, 除了需要撸之外!<br>我用过国产的单兵净水器<img src="/images/gonga/%E5%9B%BD%E4%BA%A7%E5%8D%95%E5%85%B5%E5%87%80%E6%B0%B4%E5%99%A8.png" alt="国产单兵净水器"><br>美帝的Aquamira Frontier Pro <img src="/images/gonga/Aquamira.png" alt="Aquamira"><br>还有朋友的Sawyer MINI Filter <img src="/images/gonga/%E7%B4%A2%E8%80%B6.png" alt="Sawyer"><br>我要说的是, MSR Trailshot Microfilter 是我用过最好用的净水器. 好用在于它出水量确实是非常大, 大到可以合理使用的地步了, 与上面那些尿滴漏流量妖艳贱货们完全不一样! 这次我全程除了灌保温瓶之外, 没有烧过路途中的饮用水, 完全靠这个净水器直接过滤溪水后装袋饮用. 因其出水量大, 每次包括下下包净水水收包装袋所需时间不过五分钟以内, 到达可以合理使用的范围了. 当然缺点也是有的, 要是要撸撸撸, 一次过滤一升的路途饮用水还好, 过滤超过三升的营地用水就要左手撸完右手撸, 右手撸完双手撸了.</p><h3 id="伏来阳太阳能板"><a href="#伏来阳太阳能板" class="headerlink" title="伏来阳太阳能板"></a>伏来阳太阳能板</h3><blockquote><p><img src="/images/gonga/%E5%A4%AA%E9%98%B3%E8%83%BD%E6%9D%BF.JPG" alt="太阳能板"></p><p>向着太阳奔跑的中老年们!</p></blockquote><p>高原的阳光有多强, 这块板子就有多强. 这板子刚拿到手时, 在南方的多云天表现的非常不好, 于是我在微信上与伏来阳的客服撕逼. 后来端午节出去, 在南方的大太阳下表现不错, 我对它映像改观了不少. 而这次的贡嘎之行, 这板子让我感受到了高原阳光的可怕.<br>它曾经在路餐休息的三十分钟内, 充了iphone6s百分二十以上的电. 直接从百分六十电量变成了百分八十. 它也在营地大晒场的两个多小时内, 将一节全空的3400毫安18650充到满, 然后又充了另一个3400毫安全空的18650到大约快一半. 这表现可以算是惊人, 于是我就把它背背包后面了, 于是我们一路向东而去, 于是尼玛全程晒不到太阳啊! 少年们, 如果你不是一路背着太阳的话, 推荐还是不要背在背包上了, 营地或者中间休息时充一下就很不错了, 路途上实际是充不到什么电的.<br>在这次使用完后, 个人认为在阳光充沛的地方, 太阳能板已经可以达到做作户外电源系统一部分的程度了. 大家综合一下自己要去的路线时间长短, 天气好坏, 阳光充沛程度后考虑是否将太阳能板加入装备列表中.</p><p>** 全文完, 谢谢收看! **</p>]]></content>
<summary type="html"><p>这次贡嘎之行, 因某些意外加上老天配合, 于是晴天, 下雨, 冰雹, 下雪再加雨中夜路全都遇到了, 倒也是对装备全面的检测, 对我这个装备党来说, 苦逼是苦逼了一些, 但能看到自己找来的装备的好坏倒也是一种奇葩的乐趣.</p>
<p><img src="/images/gonga/%E8%90%A5%E5%9C%B0%E6%99%92%E8%A3%85%E5%A4%87.JPG" alt="苦逼营地晒装备"></p></summary>
</entry>
<entry>
<title>贡嘎装备计划</title>
<link href="http://horsefaced.github.io/2017/10/02/%E8%B4%A1%E5%98%8E%E8%A3%85%E5%A4%87/"/>
<id>http://horsefaced.github.io/2017/10/02/%E8%B4%A1%E5%98%8E%E8%A3%85%E5%A4%87/</id>
<published>2017-10-01T22:42:28.000Z</published>
<updated>2020-09-06T07:45:09.626Z</updated>
<content type="html"><![CDATA[<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"><colgroup><col class="org-left" /><col class="org-left" /><col class="org-right" /></colgroup><thead><tr><th scope="col" class="org-left">类别</th><th scope="col" class="org-left">名称</th><th scope="col" class="org-right">重量</th></tr></thead><tbody><tr><td class="org-left">露营</td><td class="org-left">田野睡袋</td><td class="org-right">872</td></tr><tr><td class="org-left"> </td><td class="org-left">therm-a-rest xlite</td><td class="org-right">357</td></tr><tr><td class="org-left"> </td><td class="org-left">AMK Bivvy (当地布)</td><td class="org-right">101</td></tr><tr><td class="org-left"> </td><td class="org-left">酱铺2人Cuben金字塔(风绳绑在帐篷上)</td><td class="org-right">581</td></tr><tr><td class="org-left"> </td><td class="org-left">钛地钉(17根,含地钉袋)</td><td class="org-right">255</td></tr><tr><td class="org-left"> </td><td class="org-left">充气垫气泵(兼柴火炉鼓风机)</td><td class="org-right">58</td></tr><tr><td class="org-left"> </td><td class="org-left">酱铺三明治(含两个腰包一个肩包)</td><td class="org-right">1010</td></tr></tbody><tbody><tr><td class="org-left">锅炉</td><td class="org-left">柴火炉(兼酒精棉球炉, 含收纳袋)</td><td class="org-right">147</td></tr><tr><td class="org-left"> </td><td class="org-left">1.5升 铝锅(含锡箔锅盖,碳纤锅柄,收纳袋)</td><td class="org-right">105</td></tr><tr><td class="org-left"> </td><td class="org-left">钛筷与钛勺</td><td class="org-right">35</td></tr><tr><td class="org-left"> </td><td class="org-left">Flod-A-Cup 折叠水杯</td><td class="org-right">50</td></tr><tr><td class="org-left"> </td><td class="org-left">2升鸭嘴兽水袋</td><td class="org-right">36</td></tr><tr><td class="org-left"> </td><td class="org-left">1升鸭嘴兽水袋</td><td class="org-right">36</td></tr><tr><td class="org-left"> </td><td class="org-left">膳魔师FEK800</td><td class="org-right">357</td></tr><tr><td class="org-left"> </td><td class="org-left">MSR TrailShot 净水器</td><td class="org-right">150</td></tr></tbody><tbody><tr><td class="org-left">衣服</td><td class="org-left">smartwood 袜子</td><td class="org-right">68</td></tr><tr><td class="org-left"> </td><td class="org-left">outdome羊毛袜子</td><td class="org-right">60</td></tr><tr><td class="org-left"> </td><td class="org-left">迪卡侬速干长裤</td><td class="org-right">320</td></tr><tr><td class="org-left"> </td><td class="org-left">天石羽绒裤</td><td class="org-right">260</td></tr><tr><td class="org-left"> </td><td class="org-left">三峰防雨裙</td><td class="org-right">72</td></tr><tr><td class="org-left"> </td><td class="org-left">迪卡侬羊毛T恤</td><td class="org-right">165</td></tr><tr><td class="org-left"> </td><td class="org-left">老鼠神衣</td><td class="org-right">272</td></tr><tr><td class="org-left"> </td><td class="org-left">始祖鸟冲锋衣</td><td class="org-right">340</td></tr><tr><td class="org-left"> </td><td class="org-left">HW羽绒服</td><td class="org-right">340</td></tr><tr><td class="org-left"> </td><td class="org-left">buff防风头巾</td><td class="org-right">80</td></tr><tr><td class="org-left"> </td><td class="org-left">迪卡侬防风手套</td><td class="org-right">82</td></tr><tr><td class="org-left"> </td><td class="org-left">羽绒脚套</td><td class="org-right">152</td></tr><tr><td class="org-left"> </td><td class="org-left">紧身抓绒内衣</td><td class="org-right">250</td></tr><tr><td class="org-left"> </td><td class="org-left">紧身抓绒裤</td><td class="org-right">195</td></tr><tr><td class="org-left"> </td><td class="org-left">内裤</td><td class="org-right">88</td></tr></tbody><tbody><tr><td class="org-left">电器</td><td class="org-left">伏来阳太阳能板</td><td class="org-right">207</td></tr><tr><td class="org-left"> </td><td class="org-left">18650电池(3个)</td><td class="org-right">141</td></tr><tr><td class="org-left"> </td><td class="org-left">米勒 ML101</td><td class="org-right">33</td></tr><tr><td class="org-left"> </td><td class="org-left">斑马18650头灯(含一个18650电池)</td><td class="org-right">114</td></tr><tr><td class="org-left"> </td><td class="org-left">小米双头充电器</td><td class="org-right">61</td></tr><tr><td class="org-left"> </td><td class="org-left">苹果Lightning线</td><td class="org-right">8</td></tr><tr><td class="org-left"> </td><td class="org-left">garmin充电线</td><td class="org-right">15</td></tr><tr><td class="org-left"> </td><td class="org-left">microUSB线</td><td class="org-right">19</td></tr><tr><td class="org-left"> </td><td class="org-left">iphone6s</td><td class="org-right">152</td></tr></tbody><tbody><tr><td class="org-left">杂项</td><td class="org-left">便便铲</td><td class="org-right">16</td></tr><tr><td class="org-left"> </td><td class="org-left">牙刷 牙膏 毛巾</td><td class="org-right">90</td></tr><tr><td class="org-left"> </td><td class="org-left">太阳镜</td><td class="org-right">32</td></tr><tr><td class="org-left"> </td><td class="org-left">buff太阳帽</td><td class="org-right">30</td></tr><tr><td class="org-left"> </td><td class="org-left">救生哨加打火棒</td><td class="org-right">33</td></tr><tr><td class="org-left"> </td><td class="org-left">garmin forunner 935</td><td class="org-right">50</td></tr><tr><td class="org-left"> </td><td class="org-left">酱铺粗苯收纳袋\*2</td><td class="org-right">18</td></tr><tr><td class="org-left"> </td><td class="org-left">三峰网袋(中号)</td><td class="org-right">13</td></tr><tr><td class="org-left"> </td><td class="org-left">三峰网袋(大号)</td><td class="org-right">17</td></tr><tr><td class="org-left"> </td><td class="org-left">雨伞</td><td class="org-right">195</td></tr></tbody><tbody><tr><td class="org-left">总计</td><td class="org-left"> </td><td class="org-right">7266</td></tr></tbody></table>]]></content>
<summary type="html"><table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-le</summary>
</entry>
<entry>
<title>环台装备清单</title>
<link href="http://horsefaced.github.io/2017/04/16/%E7%8E%AF%E5%8F%B0%E8%A3%85%E5%A4%87%E6%B8%85%E5%8D%95/"/>
<id>http://horsefaced.github.io/2017/04/16/%E7%8E%AF%E5%8F%B0%E8%A3%85%E5%A4%87%E6%B8%85%E5%8D%95/</id>
<published>2017-04-15T16:00:00.000Z</published>
<updated>2017-10-11T13:15:36.000Z</updated>
<content type="html"><![CDATA[<table><thead><tr><th>品类</th><th>名字</th><th>数量</th></tr></thead><tbody><tr><td>衣服(数量包括身上)</td><td>速干长裤</td><td>1</td></tr><tr><td></td><td>速干短裤</td><td>2</td></tr><tr><td></td><td>速干内裤</td><td>3</td></tr><tr><td></td><td>袜子</td><td>3</td></tr><tr><td></td><td>皮肤风衣</td><td>1</td></tr><tr><td></td><td>迪卡侬雨衣</td><td>1</td></tr><tr><td></td><td>速干短袖</td><td>2</td></tr><tr><td>修理工具</td><td>撬胎棒</td><td>2</td></tr><tr><td></td><td>截链器</td><td>1</td></tr><tr><td></td><td>魔术扣</td><td>2</td></tr><tr><td></td><td>补胎片</td><td>6</td></tr><tr><td></td><td>内胎</td><td>2</td></tr><tr><td></td><td>外胎</td><td>2</td></tr><tr><td></td><td>集成工具</td><td>1</td></tr><tr><td></td><td>打气桶</td><td>1</td></tr><tr><td></td><td>链条油</td><td>1</td></tr><tr><td>日常用品</td><td>毛巾</td><td>1</td></tr><tr><td></td><td>牙刷牙膏</td><td>1</td></tr><tr><td>电器</td><td>充电头</td><td>1</td></tr><tr><td></td><td>相机</td><td>1</td></tr><tr><td></td><td>18650电池</td><td>4</td></tr><tr><td></td><td>米勒101</td><td>1</td></tr><tr><td></td><td>斑马手电</td><td>1</td></tr><tr><td></td><td>micro-usb线</td><td>2</td></tr><tr><td></td><td>苹果线</td><td>1</td></tr><tr><td></td><td>iwatch线</td><td>1</td></tr><tr><td></td><td>nokia黑白机</td><td>1</td></tr><tr><td></td><td>苹果6s</td><td>1</td></tr></tbody></table>]]></content>
<summary type="html"><table>
<thead>
<tr>
<th>品类</th>
<th>名字</th>
<th>数量</th>
</tr>
</thead>
<tbody><tr>
<td>衣服(数量包括身上)</td>
<td>速干长裤</td>
<td>1</td>
</tr>
<tr>
</summary>
<category term="旅行" scheme="http://horsefaced.github.io/categories/%E6%97%85%E8%A1%8C/"/>
<category term="旅行" scheme="http://horsefaced.github.io/tags/%E6%97%85%E8%A1%8C/"/>
<category term="台湾" scheme="http://horsefaced.github.io/tags/%E5%8F%B0%E6%B9%BE/"/>
<category term="自行车" scheme="http://horsefaced.github.io/tags/%E8%87%AA%E8%A1%8C%E8%BD%A6/"/>
</entry>
<entry>
<title>环台路书</title>
<link href="http://horsefaced.github.io/2017/04/16/%E7%8E%AF%E5%8F%B0%E8%B7%AF%E4%B9%A6/"/>
<id>http://horsefaced.github.io/2017/04/16/%E7%8E%AF%E5%8F%B0%E8%B7%AF%E4%B9%A6/</id>
<published>2017-04-15T16:00:00.000Z</published>
<updated>2017-10-11T13:15:24.000Z</updated>
<content type="html"><![CDATA[<h2 id="台北至新竹-81公里"><a href="#台北至新竹-81公里" class="headerlink" title="台北至新竹 81公里"></a>台北至新竹 81公里</h2><p>台北-三峡-大溪老街-石门-关西-城隍庙-新竹</p><p>台3线-县道118</p><p>景点:新竹城隍庙</p><h2 id="新竹至台中-100公里"><a href="#新竹至台中-100公里" class="headerlink" title="新竹至台中 100公里"></a>新竹至台中 100公里</h2><p>新竹-竹南-后茏-通霄-苑里-大甲-清水-沙鹿-台中</p><p>台61线-台1线-台12线</p><p>景点:</p><p> 大甲镇澜宫</p><p> 台中彩虹村</p><p> 台中逢甲夜市</p><h2 id="台中至嘉义-94-3公里"><a href="#台中至嘉义-94-3公里" class="headerlink" title="台中至嘉义 94.3公里"></a>台中至嘉义 94.3公里</h2><p>台中-彰化-员林-北斗-西螺-斗南-民雄-嘉义</p><p>台1线</p><p>景点:嘉义火鸡肉饭</p><h2 id="嘉义至垦丁"><a href="#嘉义至垦丁" class="headerlink" title="嘉义至垦丁"></a>嘉义至垦丁</h2><p>备用路线:</p><h3 id="去国圣灯塔"><a href="#去国圣灯塔" class="headerlink" title="去国圣灯塔"></a>去国圣灯塔</h3><h4 id="嘉义至台南-95-2公里"><a href="#嘉义至台南-95-2公里" class="headerlink" title="嘉义至台南 95.2公里"></a>嘉义至台南 95.2公里</h4><p>嘉义-国圣港灯塔-台南</p><h4 id="台南至枋寮-86-5公里"><a href="#台南至枋寮-86-5公里" class="headerlink" title="台南至枋寮 86.5公里"></a>台南至枋寮 86.5公里</h4><p>台1</p><h4 id="枋寮至垦丁-59-1公里"><a href="#枋寮至垦丁-59-1公里" class="headerlink" title="枋寮至垦丁 59.1公里"></a>枋寮至垦丁 59.1公里</h4><h3 id="不去国圣灯塔"><a href="#不去国圣灯塔" class="headerlink" title="不去国圣灯塔"></a>不去国圣灯塔</h3><h4 id="嘉义至高雄-115公里"><a href="#嘉义至高雄-115公里" class="headerlink" title="嘉义至高雄 115公里"></a>嘉义至高雄 115公里</h4><p>嘉义-水上-新营-林凤营-隆田-善化-永康-台南-冈山-高雄</p><p>台1线</p><h4 id="高雄至垦丁-104公里"><a href="#高雄至垦丁-104公里" class="headerlink" title="高雄至垦丁 104公里"></a>高雄至垦丁 104公里</h4><p>高雄-小港-东港-佳冬-枋寮-枋山-枫港-车城-恒春-垦丁</p><p>台17线-台1线-台26线</p><h2 id="垦丁至台东-153公里"><a href="#垦丁至台东-153公里" class="headerlink" title="垦丁至台东 153公里"></a>垦丁至台东 153公里</h2><p>备用路线:</p><ol><li>垦丁至旭海 58公里 游玩路线</li><li>旭海至台东 100公里</li></ol><p>台26-县200甲-县199-台9</p><h2 id="台东至玉里-85公里"><a href="#台东至玉里-85公里" class="headerlink" title="台东至玉里 85公里"></a>台东至玉里 85公里</h2><p>台东-初鹿-鹿野-关山-池上-富里-玉里</p><p>台9</p><p>景点:池上便当</p><h2 id="玉里至花莲太鲁阁-113公里"><a href="#玉里至花莲太鲁阁-113公里" class="headerlink" title="玉里至花莲太鲁阁 113公里"></a>玉里至花莲太鲁阁 113公里</h2><p>玉里-瑞穗-富源-光复糖厂-凤林-丰田-志学-吉安-花莲-新城-太鲁阁</p><p>台9</p><p>景点:</p><p> 瑞穗北回归线</p><p> 麻糬</p><h2 id="花莲至苏澳-82公里"><a href="#花莲至苏澳-82公里" class="headerlink" title="花莲至苏澳 82公里"></a>花莲至苏澳 82公里</h2><p>台9</p><p>景点:</p><p> 苏澳冷泉</p><p> 苏澳羊羹</p><h2 id="苏澳至九份-89-1公里"><a href="#苏澳至九份-89-1公里" class="headerlink" title="苏澳至九份 89.1公里"></a>苏澳至九份 89.1公里</h2><p>苏澳-三貂角灯塔-九份</p><p> 台2线</p><h2 id="九份至台北-94-5公里"><a href="#九份至台北-94-5公里" class="headerlink" title="九份至台北 94.5公里"></a>九份至台北 94.5公里</h2><p>九份-富贵角灯塔-台北</p><p>台5线</p>]]></content>
<summary type="html"><h2 id="台北至新竹-81公里"><a href="#台北至新竹-81公里" class="headerlink" title="台北至新竹 81公里"></a>台北至新竹 81公里</h2><p>台北-三峡-大溪老街-石门-关西-城隍庙-新竹</p>
<p>台3线-县道1</summary>
<category term="旅行" scheme="http://horsefaced.github.io/categories/%E6%97%85%E8%A1%8C/"/>
<category term="旅行" scheme="http://horsefaced.github.io/tags/%E6%97%85%E8%A1%8C/"/>
<category term="台湾" scheme="http://horsefaced.github.io/tags/%E5%8F%B0%E6%B9%BE/"/>
<category term="自行车" scheme="http://horsefaced.github.io/tags/%E8%87%AA%E8%A1%8C%E8%BD%A6/"/>
</entry>
<entry>
<title>危地马拉日晒波旁</title>
<link href="http://horsefaced.github.io/2017/03/15/%E5%8D%B1%E5%9C%B0%E9%A9%AC%E6%8B%89%E6%97%A5%E6%99%92%E6%B3%A2%E6%97%81/"/>
<id>http://horsefaced.github.io/2017/03/15/%E5%8D%B1%E5%9C%B0%E9%A9%AC%E6%8B%89%E6%97%A5%E6%99%92%E6%B3%A2%E6%97%81/</id>
<published>2017-03-14T16:00:00.000Z</published>
<updated>2017-10-11T13:14:53.000Z</updated>
<content type="html"><![CDATA[<h2 id="大恕家坚果探险队"><a href="#大恕家坚果探险队" class="headerlink" title="大恕家坚果探险队"></a>大恕家坚果探险队</h2><p><img src="/images/cafe/%E5%9D%9A%E6%9E%9C%E6%8E%A2%E9%99%A9%E9%98%9F.JPG" alt="坚果探险队"></p><blockquote><p>传统波旁种,产区:韦韦特南,等级:SHB,海拔:1800-2000</p><p>中浅烘,可能的味道:坚果、榛果、核桃、红果酸甜</p></blockquote><a id="more"></a><h3 id="2017-03-15-14点13"><a href="#2017-03-15-14点13" class="headerlink" title="2017 03 15 14点13"></a>2017 03 15 14点13</h3><p><img src="/images/cafe/201703151413.JPG" alt="坑"></p><table><thead><tr><th>粉量</th><th>水温</th><th>闷蒸时长</th><th>萃取时长</th><th>萃取克数</th><th>研磨度</th></tr></thead><tbody><tr><td>30</td><td>92</td><td>90</td><td>297</td><td>400</td><td>2.5</td></tr></tbody></table><p> 醇厚度非常好,不涩,无酸,回甘悠长,无苦味。</p>]]></content>
<summary type="html"><h2 id="大恕家坚果探险队"><a href="#大恕家坚果探险队" class="headerlink" title="大恕家坚果探险队"></a>大恕家坚果探险队</h2><p><img src="/images/cafe/%E5%9D%9A%E6%9E%9C%E6%8E%A2%E9%99%A9%E9%98%9F.JPG" alt="坚果探险队"></p>
<blockquote>
<p>传统波旁种,产区:韦韦特南,等级:SHB,海拔:1800-2000</p>
<p>中浅烘,可能的味道:坚果、榛果、核桃、红果酸甜</p>
</blockquote></summary>
<category term="咖啡" scheme="http://horsefaced.github.io/categories/%E5%92%96%E5%95%A1/"/>
</entry>
<entry>
<title>苏门答腊亚齐曼特宁</title>
<link href="http://horsefaced.github.io/2017/03/15/%E8%8B%8F%E9%97%A8%E7%AD%94%E8%85%8A%E4%BA%9A%E9%BD%90%E6%9B%BC%E7%89%B9%E5%AE%81/"/>
<id>http://horsefaced.github.io/2017/03/15/%E8%8B%8F%E9%97%A8%E7%AD%94%E8%85%8A%E4%BA%9A%E9%BD%90%E6%9B%BC%E7%89%B9%E5%AE%81/</id>
<published>2017-03-14T16:00:00.000Z</published>
<updated>2017-10-11T13:15:06.000Z</updated>
<content type="html"><![CDATA[<h2 id="大恕家愿望收纳箱"><a href="#大恕家愿望收纳箱" class="headerlink" title="大恕家愿望收纳箱"></a>大恕家愿望收纳箱</h2><p><img src="/images/cafe/%E6%84%BF%E6%9C%9B%E6%94%B6%E7%BA%B3%E7%AE%B1.png" alt="愿望收纳箱"></p><blockquote><p>苏门答腊亚齐水洗曼特宁</p><p>帝比卡种, 产地亚齐,海拔1550-1700,处理:水洗,中深烘</p><p>可能尝到的味道:松子香、太妃糖、桑葚</p></blockquote><a id="more"></a><h3 id="2017-03-15-08-57"><a href="#2017-03-15-08-57" class="headerlink" title="2017 03 15 08:57"></a>2017 03 15 08:57</h3><table><thead><tr><th>粉量(克)</th><th>水温</th><th>闷蒸时长</th><th>萃取时长</th><th>萃取克数</th></tr></thead><tbody><tr><td>15</td><td>90</td><td>45</td><td>150</td><td>200</td></tr></tbody></table><p>入口酸香,有麦芽糖的甜味,酸甜震明显,后味甜感悠长。</p><p>温度下降,酸质明显,有紧的感觉,甜感下降,酸甜震减弱,后味有涩感。</p><p>温度再下降,奶油味出现</p><h3 id="2017-03-20-08-59"><a href="#2017-03-20-08-59" class="headerlink" title="2017 03 20 08:59"></a>2017 03 20 08:59</h3><p><img src="/images/cafe/201703200900.JPG" alt="坑"></p><table><thead><tr><th>粉量(克)</th><th>水温</th><th>闷蒸时长</th><th>萃取时长</th><th>萃取克数</th></tr></thead><tbody><tr><td>15</td><td>90</td><td>60</td><td>180</td><td>200</td></tr></tbody></table><p>这次冲的不好喝,杂味过重。没必要用滴滴法,或者是水温太高,可能降到85度再用滴滴会更好,现在这个水温过萃了。</p>]]></content>
<summary type="html"><h2 id="大恕家愿望收纳箱"><a href="#大恕家愿望收纳箱" class="headerlink" title="大恕家愿望收纳箱"></a>大恕家愿望收纳箱</h2><p><img src="/images/cafe/%E6%84%BF%E6%9C%9B%E6%94%B6%E7%BA%B3%E7%AE%B1.png" alt="愿望收纳箱"></p>
<blockquote>
<p>苏门答腊亚齐水洗曼特宁</p>
<p>帝比卡种, 产地亚齐,海拔1550-1700,处理:水洗,中深烘</p>
<p>可能尝到的味道:松子香、太妃糖、桑葚</p>
</blockquote></summary>
<category term="咖啡" scheme="http://horsefaced.github.io/categories/%E5%92%96%E5%95%A1/"/>
</entry>
</feed>