ffmepg源码之http学习笔记

respect

致敬雷神

致敬雷神

start

从 AVFormatContext (解封装结构体) 入口开始吧 …

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
libavformat/options.c

AVFormatContext *avformat_alloc_context(void)
{
FFFormatContext *const si = av_mallocz(sizeof(*si));
AVFormatContext *s;

if (!si)
return NULL;

s = &si->pub;
s->av_class = &av_format_context_class;
/// io入口
s->io_open = io_open_default;
s->io_close = ff_format_io_close_default;
s->io_close2= io_close2_default;

av_opt_set_defaults(s);

si->pkt = av_packet_alloc();
si->parse_pkt = av_packet_alloc();
if (!si->pkt || !si->parse_pkt) {
avformat_free_context(s);
return NULL;
}

si->shortest_end = AV_NOPTS_VALUE;

return s;
}
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
libavformat/demux.c

int avformat_open_input(AVFormatContext **ps, const char *filename,
const AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
FFFormatContext *si;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
int ret = 0;

if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
si = ffformatcontext(s);
if (!s->av_class) {
av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
return AVERROR(EINVAL);
}
if (fmt)
s->iformat = fmt;

if (options)
av_dict_copy(&tmp, *options, 0);

if (s->pb) // must be before any goto fail
s->flags |= AVFMT_FLAG_CUSTOM_IO;

if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;

if (!(s->url = av_strdup(filename ? filename : ""))) {
ret = AVERROR(ENOMEM);
goto fail;
}

/// 初始化输入流的信息。这里会初始化AVInputFormat
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
s->probe_score = ret;

if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
if (!s->protocol_whitelist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}

if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
if (!s->protocol_blacklist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}

if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
ret = AVERROR(EINVAL);
goto fail;
}

avio_skip(s->pb, s->skip_initial_bytes);

/* Check filename in case an image number is expected. */
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(filename)) {
ret = AVERROR(EINVAL);
goto fail;
}
}

s->duration = s->start_time = AV_NOPTS_VALUE;

/* Allocate private data. */
if (s->iformat->priv_data_size > 0) {
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (s->iformat->priv_class) {
*(const AVClass **) s->priv_data = s->iformat->priv_class;
av_opt_set_defaults(s->priv_data);
if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
goto fail;
}
}

/* e.g. AVFMT_NOFILE formats will not have an AVIOContext */
if (s->pb)
ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);

if (s->iformat->read_header)
if ((ret = s->iformat->read_header(s)) < 0) {
if (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP)
goto close;
goto fail;
}

if (!s->metadata) {
s->metadata = si->id3v2_meta;
si->id3v2_meta = NULL;
} else if (si->id3v2_meta) {
av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n");
av_dict_free(&si->id3v2_meta);
}

if (id3v2_extra_meta) {
if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
!strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)
goto close;
if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)
goto close;
if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0)
goto close;
} else
av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
}

if ((ret = avformat_queue_attached_pictures(s)) < 0)
goto close;

if (s->pb && !si->data_offset)
si->data_offset = avio_tell(s->pb);

si->raw_packet_buffer_size = 0;

update_stream_avctx(s);

if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;

close:
if (s->iformat->read_close)
s->iformat->read_close(s);
fail:
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
avio_closep(&s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}
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
libavformat/demux.c

static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
int score = AVPROBE_SCORE_RETRY;

///初始化的过程 s->pb 还没创建好
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if (!s->iformat)
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
else if (s->iformat->flags & AVFMT_NOFILE)
av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
"will be ignored with AVFMT_NOFILE format.\n");
return 0;
}

/* Guess file format. */
// av_probe_input_format2 首次判断inputformat格式 iformat,这一步通常判断不出来
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;

/// io_open 就是前面赋值的入口函数 io_open_default
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
return ret;

if (s->iformat)
return 0;

// av_probe_input_buffer2再次判断inputformat格式 iformat,通过buf媒体流数据判断。
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
libavformat/options.c

static int io_open_default(AVFormatContext *s, AVIOContext **pb,
const char *url, int flags, AVDictionary **options)
{
int loglevel;

if (!strcmp(url, s->url) ||
s->iformat && !strcmp(s->iformat->name, "image2") ||
s->oformat && !strcmp(s->oformat->name, "image2")
) {
loglevel = AV_LOG_DEBUG;
} else
loglevel = AV_LOG_INFO;

av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");

/// io-open
return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
}
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
libavformat/aviobuf.c

int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
const char *whitelist, const char *blacklist
)
{
URLContext *h;
int err;

*s = NULL;

/// 创建 URLContext
err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
if (err < 0)
return err;
/// 创建AVIOContext 赋值 urlcontext
err = ffio_fdopen(s, h);
if (err < 0) {
ffurl_close(h);
return err;
}
return 0;
}

```c
libavformat/avio.c

int ffurl_alloc(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb)
{
const URLProtocol *p = NULL;
/// 根据filename 找到 http
p = url_find_protocol(filename);
if (p)
/// 创建URLContext 并赋值 URLProtocol
return url_alloc_for_protocol(puc, p, filename, flags, int_cb);

*puc = NULL;
return AVERROR_PROTOCOL_NOT_FOUND;
}

static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,
const char *filename, int flags,
const AVIOInterruptCB *int_cb)
{
URLContext *uc;
int err;

#if CONFIG_NETWORK
if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
return AVERROR(EIO);
#endif
if ((flags & AVIO_FLAG_READ) && !up->url_read) {
av_log(NULL, AV_LOG_ERROR,
"Impossible to open the '%s' protocol for reading\n", up->name);
return AVERROR(EIO);
}
if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {
av_log(NULL, AV_LOG_ERROR,
"Impossible to open the '%s' protocol for writing\n", up->name);
return AVERROR(EIO);
}
/// 创建 URLContext
uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
if (!uc) {
err = AVERROR(ENOMEM);
goto fail;
}
uc->av_class = &ffurl_context_class;
uc->filename = (char *)&uc[1];
strcpy(uc->filename, filename);
/// 赋值 urlprotocal
uc->prot = up;
uc->flags = flags;
uc->is_streamed = 0; /* default = not streamed */
uc->max_packet_size = 0; /* default: stream file */
/// httpprotocal 设置过了 priv_data 为 HTTPContext
if (up->priv_data_size) {
uc->priv_data = av_mallocz(up->priv_data_size);
if (!uc->priv_data) {
err = AVERROR(ENOMEM);
goto fail;
}
if (up->priv_data_class) {
char *start;
*(const AVClass **)uc->priv_data = up->priv_data_class;
av_opt_set_defaults(uc->priv_data);
if (av_strstart(uc->filename, up->name, (const char**)&start) && *start == ',') {
int ret= 0;
char *p= start;
char sep= *++p;
char *key, *val;
p++;

if (strcmp(up->name, "subfile"))
ret = AVERROR(EINVAL);

while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
*val= *key= 0;
if (strcmp(p, "start") && strcmp(p, "end")) {
ret = AVERROR_OPTION_NOT_FOUND;
} else
ret= av_opt_set(uc->priv_data, p, key+1, 0);
if (ret == AVERROR_OPTION_NOT_FOUND)
av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
*val= *key= sep;
p= val+1;
}
if(ret<0 || p!=key){
av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
av_freep(&uc->priv_data);
av_freep(&uc);
err = AVERROR(EINVAL);
goto fail;
}
memmove(start, key+1, strlen(key));
}
}
}
if (int_cb)
uc->interrupt_callback = *int_cb;

*puc = uc;
return 0;
fail:
*puc = NULL;
if (uc)
av_freep(&uc->priv_data);
av_freep(&uc);
#if CONFIG_NETWORK
if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
ff_network_close();
#endif
return err;
}

int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
const char *whitelist, const char* blacklist,
URLContext *parent)
{
AVDictionary *tmp_opts = NULL;
AVDictionaryEntry *e;
/// 创建 URLContext
int ret = ffurl_alloc(puc, filename, flags, int_cb);
if (ret < 0)
return ret;
if (parent) {
ret = av_opt_copy(*puc, parent);
if (ret < 0)
goto fail;
}
if (options &&
(ret = av_opt_set_dict(*puc, options)) < 0)
goto fail;
if (options && (*puc)->prot->priv_data_class &&
(ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
goto fail;

if (!options)
options = &tmp_opts;

av_assert0(!whitelist ||
!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
!strcmp(whitelist, e->value));
av_assert0(!blacklist ||
!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
!strcmp(blacklist, e->value));

if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
goto fail;

if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
goto fail;

if ((ret = av_opt_set_dict(*puc, options)) < 0)
goto fail;

/// 发起链接
ret = ffurl_connect(*puc, options);

if (!ret)
return 0;
fail:
ffurl_closep(puc);
return ret;
}
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
libavformat/avio.c

int ffurl_connect(URLContext *uc, AVDictionary **options)
{
int err;
AVDictionary *tmp_opts = NULL;
AVDictionaryEntry *e;

if (!options)
options = &tmp_opts;

// Check that URLContext was initialized correctly and lists are matching if set
av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
(uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value)));
av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
(uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value)));

if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {
av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist);
return AVERROR(EINVAL);
}

if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) {
av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist);
return AVERROR(EINVAL);
}

if (!uc->protocol_whitelist && uc->prot->default_whitelist) {
av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist);
uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist);
if (!uc->protocol_whitelist) {
return AVERROR(ENOMEM);
}
} else if (!uc->protocol_whitelist)
av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist

if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0)
return err;
if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0)
return err;

err =
/// uc.prot 是 http url_open2 对应的是 http_open
uc->prot->url_open2 ? uc->prot->url_open2(uc,
uc->filename,
uc->flags,
options) :
uc->prot->url_open(uc, uc->filename, uc->flags);

av_dict_set(options, "protocol_whitelist", NULL, 0);
av_dict_set(options, "protocol_blacklist", NULL, 0);

if (err)
return err;
uc->is_connected = 1;
/* We must be careful here as ffurl_seek() could be slow,
* for example for http */
if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file"))
if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
uc->is_streamed = 1;
return 0;
}
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
libavformat/aviobuf.c

int ffio_fdopen(AVIOContext **s, URLContext *h)
{
uint8_t *buffer = NULL;
int buffer_size, max_packet_size;

max_packet_size = h->max_packet_size;
if (max_packet_size) {
buffer_size = max_packet_size; /* no need to bufferize more than one packet */
} else {
buffer_size = IO_BUFFER_SIZE;
}
if (!(h->flags & AVIO_FLAG_WRITE) && h->is_streamed) {
if (buffer_size > INT_MAX/2)
return AVERROR(EINVAL);
buffer_size *= 2;
}
buffer = av_malloc(buffer_size);
if (!buffer)
return AVERROR(ENOMEM);

/// 创建 AVIOContext
/// s->opaque = h;
*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
(int (*)(void *, uint8_t *, int)) ffurl_read,
(int (*)(void *, uint8_t *, int)) ffurl_write,
(int64_t (*)(void *, int64_t, int))ffurl_seek);
if (!*s) {
av_freep(&buffer);
return AVERROR(ENOMEM);
}
(*s)->protocol_whitelist = av_strdup(h->protocol_whitelist);
if (!(*s)->protocol_whitelist && h->protocol_whitelist) {
avio_closep(s);
return AVERROR(ENOMEM);
}
(*s)->protocol_blacklist = av_strdup(h->protocol_blacklist);
if (!(*s)->protocol_blacklist && h->protocol_blacklist) {
avio_closep(s);
return AVERROR(ENOMEM);
}
(*s)->direct = h->flags & AVIO_FLAG_DIRECT;

(*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;
(*s)->max_packet_size = max_packet_size;
(*s)->min_packet_size = h->min_packet_size;
if(h->prot) {
(*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause;
(*s)->read_seek =
(int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek;

if (h->prot->url_read_seek)
(*s)->seekable |= AVIO_SEEKABLE_TIME;
}
((FFIOContext*)(*s))->short_seek_get = (int (*)(void *))ffurl_get_short_seek;
(*s)->av_class = &ff_avio_class;
return 0;
}
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
libavformat/http.c

/// 这个就是对应前面的 protocol
const URLProtocol ff_http_protocol = {
.name = "http",
/// *
.url_open2 = http_open,
.url_accept = http_accept,
.url_handshake = http_handshake,
.url_read = http_read,
.url_write = http_write,
.url_seek = http_seek,
.url_close = http_close,
.url_get_file_handle = http_get_file_handle,
.url_get_short_seek = http_get_short_seek,
.url_shutdown = http_shutdown,
/// *
.priv_data_size = sizeof(HTTPContext),
.priv_data_class = &http_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
.default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy,data"
};

static int http_open(URLContext *h, const char *uri, int flags,
AVDictionary **options)
{
HTTPContext *s = h->priv_data;
int ret;

if( s->seekable == 1 )
h->is_streamed = 0;
else
h->is_streamed = 1;

s->filesize = UINT64_MAX;

s->location = av_strdup(uri);
if (!s->location)
return AVERROR(ENOMEM);

s->uri = av_strdup(uri);
if (!s->uri)
return AVERROR(ENOMEM);

if (options)
av_dict_copy(&s->chained_options, *options, 0);

if (s->headers) {
int len = strlen(s->headers);
if (len < 2 || strcmp("\r\n", s->headers + len - 2)) {
av_log(h, AV_LOG_WARNING,
"No trailing CRLF found in HTTP header. Adding it.\n");
ret = av_reallocp(&s->headers, len + 3);
if (ret < 0)
goto bail_out;
s->headers[len] = '\r';
s->headers[len + 1] = '\n';
s->headers[len + 2] = '\0';
}
}

if (s->listen) {
return http_listen(h, uri, flags, options);
}
/// 进这里
ret = http_open_cnx(h, options);
bail_out:
if (ret < 0) {
av_dict_free(&s->chained_options);
av_dict_free(&s->cookie_dict);
av_dict_free(&s->redirect_cache);
av_freep(&s->new_location);
av_freep(&s->uri);
}
return ret;
}


static int http_open_cnx(URLContext *h, AVDictionary **options)
{
HTTPAuthType cur_auth_type, cur_proxy_auth_type;
HTTPContext *s = h->priv_data;
int ret, attempts = 0, redirects = 0;
int reconnect_delay = 0;
uint64_t off;
char *cached;

redo:

cached = redirect_cache_get(s);
if (cached) {
av_free(s->location);
s->location = av_strdup(cached);
if (!s->location) {
ret = AVERROR(ENOMEM);
goto fail;
}
goto redo;
}

av_dict_copy(options, s->chained_options, 0);

cur_auth_type = s->auth_state.auth_type;
cur_proxy_auth_type = s->auth_state.auth_type;

off = s->off;
/// 进这里 关键方法
ret = http_open_cnx_internal(h, options);
if (ret < 0) {
if (!http_should_reconnect(s, ret) ||
reconnect_delay > s->reconnect_delay_max)
goto fail;

av_log(h, AV_LOG_WARNING, "Will reconnect at %"PRIu64" in %d second(s).\n", off, reconnect_delay);
ret = ff_network_sleep_interruptible(1000U * 1000 * reconnect_delay, &h->interrupt_callback);
if (ret != AVERROR(ETIMEDOUT))
goto fail;
reconnect_delay = 1 + 2 * reconnect_delay;

/* restore the offset (http_connect resets it) */
s->off = off;

ffurl_closep(&s->hd);
goto redo;
}

attempts++;
if (s->http_code == 401) {
if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
ffurl_closep(&s->hd);
goto redo;
} else
goto fail;
}
if (s->http_code == 407) {
if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
ffurl_closep(&s->hd);
goto redo;
} else
goto fail;
}
if ((s->http_code == 301 || s->http_code == 302 ||
s->http_code == 303 || s->http_code == 307 || s->http_code == 308) &&
s->new_location) {
/* url moved, get next */
ffurl_closep(&s->hd);
if (redirects++ >= MAX_REDIRECTS)
return AVERROR(EIO);

if (!s->expires) {
s->expires = (s->http_code == 301 || s->http_code == 308) ? INT64_MAX : -1;
}

if (s->expires > time(NULL) && av_dict_count(s->redirect_cache) < MAX_CACHED_REDIRECTS) {
redirect_cache_set(s, s->location, s->new_location, s->expires);
}

av_free(s->location);
s->location = s->new_location;
s->new_location = NULL;

/* Restart the authentication process with the new target, which
* might use a different auth mechanism. */
memset(&s->auth_state, 0, sizeof(s->auth_state));
attempts = 0;
goto redo;
}
return 0;

fail:
if (s->hd)
ffurl_closep(&s->hd);
if (ret < 0)
return ret;
return ff_http_averror(s->http_code, AVERROR(EIO));
}


static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
{
/// lower_proto 关键
const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
char *env_http_proxy, *env_no_proxy;
char *hashmark;
char hostname[1024], hoststr[1024], proto[10];
char auth[1024], proxyauth[1024] = "";
char path1[MAX_URL_SIZE], sanitized_path[MAX_URL_SIZE + 1];
char buf[1024], urlbuf[MAX_URL_SIZE];
int port, use_proxy, err = 0;
/// 前面定义httpprotocal的时候设置过了
HTTPContext *s = h->priv_data;

av_url_split(proto, sizeof(proto), auth, sizeof(auth),
hostname, sizeof(hostname), &port,
path1, sizeof(path1), s->location);
ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);

env_http_proxy = getenv_utf8("http_proxy");
proxy_path = s->http_proxy ? s->http_proxy : env_http_proxy;

env_no_proxy = getenv_utf8("no_proxy");
use_proxy = !ff_http_match_no_proxy(env_no_proxy, hostname) &&
proxy_path && av_strstart(proxy_path, "http://", NULL);
freeenv_utf8(env_no_proxy);


/// 如果是https 会走到 tls (tls_securetransport.c & tls.c)
/// tls 内部 最后也会走到tcp的 先略过 先直接看tcp

if (!strcmp(proto, "https")) {
lower_proto = "tls";
use_proxy = 0;
if (port < 0)
port = 443;
/* pass http_proxy to underlying protocol */
if (s->http_proxy) {
err = av_dict_set(options, "http_proxy", s->http_proxy, 0);
if (err < 0)
goto end;
}
}
if (port < 0)
port = 80;

hashmark = strchr(path1, '#');
if (hashmark)
*hashmark = '\0';

if (path1[0] == '\0') {
path = "/";
} else if (path1[0] == '?') {
snprintf(sanitized_path, sizeof(sanitized_path), "/%s", path1);
path = sanitized_path;
} else {
path = path1;
}
local_path = path;
if (use_proxy) {
/* Reassemble the request URL without auth string - we don't
* want to leak the auth to the proxy. */
ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
path1);
path = urlbuf;
av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
}

/// 这里先以tcp来分析
ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);

if (!s->hd) {
/// s->hd 也是 URLContext,发现这里又进入了ffurl_open_whitelist 循环了
/// buf 是 tcp了 不是http 了, 否则就跳不出来了
/// 所以 s->hd 的 protocal 是 TCPProtocol
err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, options,
h->protocol_whitelist, h->protocol_blacklist, h);
}

end:
freeenv_utf8(env_http_proxy);
return err < 0 ? err : http_connect(
h, path, local_path, hoststr, auth, proxyauth);
}

static int http_connect(URLContext *h, const char *path, const char *local_path,
const char *hoststr, const char *auth,
const char *proxyauth)
{
HTTPContext *s = h->priv_data;
int post, err;
AVBPrint request;
char *authstr = NULL, *proxyauthstr = NULL;
uint64_t off = s->off;
const char *method;
int send_expect_100 = 0;

av_bprint_init_for_buffer(&request, s->buffer, sizeof(s->buffer));

/* send http header */
post = h->flags & AVIO_FLAG_WRITE;

if (s->post_data) {
/* force POST method and disable chunked encoding when
* custom HTTP post data is set */
post = 1;
s->chunked_post = 0;
}

if (s->method)
method = s->method;
else
method = post ? "POST" : "GET";

authstr = ff_http_auth_create_response(&s->auth_state, auth,
local_path, method);
proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
local_path, method);

if (post && !s->post_data) {
if (s->send_expect_100 != -1) {
send_expect_100 = s->send_expect_100;
} else {
send_expect_100 = 0;
/* The user has supplied authentication but we don't know the auth type,
* send Expect: 100-continue to get the 401 response including the
* WWW-Authenticate header, or an 100 continue if no auth actually
* is needed. */
if (auth && *auth &&
s->auth_state.auth_type == HTTP_AUTH_NONE &&
s->http_code != 401)
send_expect_100 = 1;
}
}

av_bprintf(&request, "%s ", method);
bprint_escaped_path(&request, path);
av_bprintf(&request, " HTTP/1.1\r\n");

if (post && s->chunked_post)
av_bprintf(&request, "Transfer-Encoding: chunked\r\n");
/* set default headers if needed */
if (!has_header(s->headers, "\r\nUser-Agent: "))
av_bprintf(&request, "User-Agent: %s\r\n", s->user_agent);
if (s->referer) {
/* set default headers if needed */
if (!has_header(s->headers, "\r\nReferer: "))
av_bprintf(&request, "Referer: %s\r\n", s->referer);
}
if (!has_header(s->headers, "\r\nAccept: "))
av_bprintf(&request, "Accept: */*\r\n");
// Note: we send the Range header on purpose, even when we're probing,
// since it allows us to detect more reliably if a (non-conforming)
// server supports seeking by analysing the reply headers.
if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->end_off || s->seekable != 0)) {
av_bprintf(&request, "Range: bytes=%"PRIu64"-", s->off);
if (s->end_off)
av_bprintf(&request, "%"PRId64, s->end_off - 1);
av_bprintf(&request, "\r\n");
}
if (send_expect_100 && !has_header(s->headers, "\r\nExpect: "))
av_bprintf(&request, "Expect: 100-continue\r\n");

if (!has_header(s->headers, "\r\nConnection: "))
av_bprintf(&request, "Connection: %s\r\n", s->multiple_requests ? "keep-alive" : "close");

if (!has_header(s->headers, "\r\nHost: "))
av_bprintf(&request, "Host: %s\r\n", hoststr);
if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
av_bprintf(&request, "Content-Length: %d\r\n", s->post_datalen);

if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
av_bprintf(&request, "Content-Type: %s\r\n", s->content_type);
if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) {
char *cookies = NULL;
if (!get_cookies(s, &cookies, path, hoststr) && cookies) {
av_bprintf(&request, "Cookie: %s\r\n", cookies);
av_free(cookies);
}
}
if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy)
av_bprintf(&request, "Icy-MetaData: 1\r\n");

/* now add in custom headers */
if (s->headers)
av_bprintf(&request, "%s", s->headers);

if (authstr)
av_bprintf(&request, "%s", authstr);
if (proxyauthstr)
av_bprintf(&request, "Proxy-%s", proxyauthstr);
av_bprintf(&request, "\r\n");

av_log(h, AV_LOG_DEBUG, "request: %s\n", request.str);

if (!av_bprint_is_complete(&request)) {
av_log(h, AV_LOG_ERROR, "overlong headers\n");
err = AVERROR(EINVAL);
goto done;
}

/// 写入数据 发起请求
if ((err = ffurl_write(s->hd, request.str, request.len)) < 0)
goto done;

if (s->post_data)
if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0)
goto done;

/* init input buffer */
s->buf_ptr = s->buffer;
s->buf_end = s->buffer;
s->line_count = 0;
s->off = 0;
s->icy_data_read = 0;
s->filesize = UINT64_MAX;
s->willclose = 0;
s->end_chunked_post = 0;
s->end_header = 0;
#if CONFIG_ZLIB
s->compressed = 0;
#endif
if (post && !s->post_data && !send_expect_100) {
/* Pretend that it did work. We didn't read any header yet, since
* we've still to send the POST data, but the code calling this
* function will check http_code after we return. */
s->http_code = 200;
err = 0;
goto done;
}

/* wait for header */
err = http_read_header(h);
if (err < 0)
goto done;

if (s->new_location)
s->off = off;

err = (off == s->off) ? 0 : -1;
done:
av_freep(&authstr);
av_freep(&proxyauthstr);
return err;
}
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
libavformat/avio.c

int ffurl_write(URLContext *h, const unsigned char *buf, int size)
{
if (!(h->flags & AVIO_FLAG_WRITE))
return AVERROR(EIO);
/* avoid sending too big packets */
if (h->max_packet_size && size > h->max_packet_size)
return AVERROR(EIO);

/// h->prot 通过前面的分析 就是TCPProtocol
return retry_transfer_wrapper(h, (unsigned char *)buf, size, size,
(int (*)(struct URLContext *, uint8_t *, int))
h->prot->url_write);
}

static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
int size, int size_min,
int (*transfer_func)(URLContext *h,
uint8_t *buf,
int size))
{
int ret, len;
int fast_retries = 5;
int64_t wait_since = 0;

len = 0;
while (len < size_min) {
if (ff_check_interrupt(&h->interrupt_callback))
return AVERROR_EXIT;

/// 回调
ret = transfer_func(h, buf + len, size - len);
if (ret == AVERROR(EINTR))
continue;
if (h->flags & AVIO_FLAG_NONBLOCK)
return ret;
if (ret == AVERROR(EAGAIN)) {
ret = 0;
if (fast_retries) {
fast_retries--;
} else {
if (h->rw_timeout) {
if (!wait_since)
wait_since = av_gettime_relative();
else if (av_gettime_relative() > wait_since + h->rw_timeout)
return AVERROR(EIO);
}
av_usleep(1000);
}
} else if (ret == AVERROR_EOF)
return (len > 0) ? len : AVERROR_EOF;
else if (ret < 0)
return ret;
if (ret) {
fast_retries = FFMAX(fast_retries, 2);
wait_since = 0;
}
len += ret;
}
return len;
}
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
libavformat/tcp.c

const URLProtocol ff_tcp_protocol = {
.name = "tcp",
.url_open = tcp_open,
.url_accept = tcp_accept,
.url_read = tcp_read,
.url_write = tcp_write,
.url_close = tcp_close,
.url_get_file_handle = tcp_get_file_handle,
.url_get_short_seek = tcp_get_window_size,
.url_shutdown = tcp_shutdown,
.priv_data_size = sizeof(TCPContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tcp_class,
};

static int tcp_write(URLContext *h, const uint8_t *buf, int size)
{
TCPContext *s = h->priv_data;
int ret;

if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
if (ret)
return ret;
}
/// socket send
ret = send(s->fd, buf, size, MSG_NOSIGNAL);
return ret < 0 ? ff_neterrno() : ret;
}

整体的代码流程如上,关键入口都在对应的地方加了注释

class relation

avformat 初始化 根据 url协议找到 urlprotocol, 创建urlcontext & aviocontext

三者的关系如下

1
2
3
avformatcontext.pb = aviocontext
aviocontext.opaque = urlcontext
urlcontext.prot = urlprotocol

urlprotocol privdata 指定为 httpcontext
httpcontext 内部持有的urlcontext 根据 tcp 找到tcpprotocol
httpconext.hd = urlcontext

后续进入到 tcp 发送数据的流程

SSL/TLS

上面为了快速弄清楚调用过程,当做http处理,简化了tls这一块,https早已普及,所以https 跟 tcp 中间有一层 ssl/tls

ffmpeg 内部处理tls协议的有好几个 gnutls openssl schannel securetransport libtls mbedtls
根据不用的平台保留对应的哪一个,我的设备是mac,对应的是 securetransport

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
libavformat/tls_securetransport.c

const URLProtocol ff_tls_protocol = {
.name = "tls",
.url_open2 = tls_open,
.url_read = tls_read,
.url_write = tls_write,
.url_close = tls_close,
.url_get_file_handle = tls_get_file_handle,
.url_get_short_seek = tls_get_short_seek,
.priv_data_size = sizeof(TLSContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tls_class,
};

typedef struct TLSContext {
const AVClass *class;
TLSShared tls_shared;
SSLContextRef ssl_context;
CFArrayRef ca_array;
int lastErr;
} TLSContext;

libavformat/tls.h
typedef struct TLSShared {
char *ca_file;
int verify;
char *cert_file;
char *key_file;
int listen;

char *host;
char *http_proxy;

char underlying_host[200];
int numerichost;

/// tls内部就是tcp
URLContext *tcp;
} TLSShared;

static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
{
TLSContext *c = h->priv_data;
TLSShared *s = &c->tls_shared;
int ret;

/// tcp open
if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
goto fail;

/// sslcontext 创建过程 见底部引用的苹果文档
c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
if (!c->ssl_context) {
av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
ret = AVERROR(ENOMEM);
goto fail;
}
if (s->ca_file) {
if ((ret = load_ca(h)) < 0)
goto fail;
}
if (s->ca_file || !s->verify)
CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
if (s->cert_file)
if ((ret = load_cert(h)) < 0)
goto fail;
CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
/// io 回调
CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
while (1) {
OSStatus status = SSLHandshake(c->ssl_context);
if (status == errSSLServerAuthCompleted) {
SecTrustRef peerTrust;
SecTrustResultType trustResult;
if (!s->verify)
continue;

if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
ret = AVERROR(ENOMEM);
goto fail;
}

if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
ret = AVERROR_UNKNOWN;
goto fail;
}

if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
ret = AVERROR_UNKNOWN;
goto fail;
}

if (trustResult == kSecTrustResultProceed ||
trustResult == kSecTrustResultUnspecified) {
// certificate is trusted
status = errSSLWouldBlock; // so we call SSLHandshake again
} else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
// not trusted, for some reason other than being expired
status = errSSLXCertChainInvalid;
} else {
// cannot use this certificate (fatal)
status = errSSLBadCert;
}

if (peerTrust)
CFRelease(peerTrust);
}
if (status == noErr) {
break;
} else if (status != errSSLWouldBlock) {
av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
ret = AVERROR(EIO);
goto fail;
}
}

return 0;
fail:
tls_close(h);
return ret;
}

static OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
{
URLContext *h = (URLContext*)connection;
TLSContext *c = h->priv_data;
size_t requested = *dataLength;
/// 交给下一层的tcp去处理 read
/// 这里读到的数据 还没有经过tls解码,不可读
int read = ffurl_read(c->tls_shared.tcp, data, requested);
if (read <= 0) {
*dataLength = 0;
switch(AVUNERROR(read)) {
case ENOENT:
case 0:
return errSSLClosedGraceful;
case ECONNRESET:
return errSSLClosedAbort;
case EAGAIN:
return errSSLWouldBlock;
default:
c->lastErr = read;
return ioErr;
}
} else {
*dataLength = read;
if (read < requested)
return errSSLWouldBlock;
else
return noErr;
}
}

static OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
{
URLContext *h = (URLContext*)connection;
TLSContext *c = h->priv_data;
/// 交给下一层的tcp去处理 write
/// data 已经经过的tls的加密处理了。不可读
int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
if (written <= 0) {
*dataLength = 0;
switch(AVUNERROR(written)) {
case EAGAIN:
return errSSLWouldBlock;
default:
c->lastErr = written;
return ioErr;
}
} else {
*dataLength = written;
return noErr;
}
}

static int tls_read(URLContext *h, uint8_t *buf, int size)
{
TLSContext *c = h->priv_data;
size_t available = 0, processed = 0;
int ret;
SSLGetBufferedReadSize(c->ssl_context, &available);
if (available)
size = FFMIN(available, size);
/// 读数据,这里读到的数据已经经过的tls的解密,是明文了 断点可以查看数据
ret = SSLRead(c->ssl_context, buf, size, &processed);
ret = map_ssl_error(ret, processed);
if (ret > 0)
return ret;
if (ret == 0)
return AVERROR_EOF;
return print_tls_error(h, ret);
}

static int tls_write(URLContext *h, const uint8_t *buf, int size)
{
TLSContext *c = h->priv_data;
size_t processed = 0;
/// 写数据,应用层的数据还没有经过tls的加密,断点可读
int ret = SSLWrite(c->ssl_context, buf, size, &processed);
ret = map_ssl_error(ret, processed);
if (ret > 0)
return ret;
if (ret == 0)
return AVERROR_EOF;
return print_tls_error(h, ret);
}
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
libavformat/tls.c

int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options)
{
int port;
const char *p;
char buf[200], opts[50] = "";
struct addrinfo hints = { 0 }, *ai = NULL;
const char *proxy_path;
char *env_http_proxy, *env_no_proxy;
int use_proxy;

set_options(c, uri);

if (c->listen)
snprintf(opts, sizeof(opts), "?listen=1");

av_url_split(NULL, 0, NULL, 0, c->underlying_host, sizeof(c->underlying_host), &port, NULL, 0, uri);

p = strchr(uri, '?');

if (!p) {
p = opts;
} else {
if (av_find_info_tag(opts, sizeof(opts), "listen", p))
c->listen = 1;
}

ff_url_join(buf, sizeof(buf), "tcp", NULL, c->underlying_host, port, "%s", p);

hints.ai_flags = AI_NUMERICHOST;
if (!getaddrinfo(c->underlying_host, NULL, &hints, &ai)) {
c->numerichost = 1;
freeaddrinfo(ai);
}

if (!c->host && !(c->host = av_strdup(c->underlying_host)))
return AVERROR(ENOMEM);

env_http_proxy = getenv_utf8("http_proxy");
proxy_path = c->http_proxy ? c->http_proxy : env_http_proxy;

env_no_proxy = getenv_utf8("no_proxy");
use_proxy = !ff_http_match_no_proxy(env_no_proxy, c->underlying_host) &&
proxy_path && av_strstart(proxy_path, "http://", NULL);
freeenv_utf8(env_no_proxy);

if (use_proxy) {
char proxy_host[200], proxy_auth[200], dest[200];
int proxy_port;
av_url_split(NULL, 0, proxy_auth, sizeof(proxy_auth),
proxy_host, sizeof(proxy_host), &proxy_port, NULL, 0,
proxy_path);
ff_url_join(dest, sizeof(dest), NULL, NULL, c->underlying_host, port, NULL);
ff_url_join(buf, sizeof(buf), "httpproxy", proxy_auth, proxy_host,
proxy_port, "/%s", dest);
}

freeenv_utf8(env_http_proxy);

///熟悉的方法,根据上面拼接的tcp:// 打开tcp
return ffurl_open_whitelist(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
&parent->interrupt_callback, options,
parent->protocol_whitelist, parent->protocol_blacklist, parent);
}

tls这块的处理逻辑 作为http 跟 tcp的中间层,都是遵循的URLProtocol协议,所以流程基本跟http、tcp一样。
关键点也同样加了对应的注释辅助理解,关于sslcontext 见下面的苹果文档, 如果熟悉tls的四次握手,基本还是可以理解。

summary

整个代码过程顺下来,基本就是网络协议的调用过程,http/https -> ssl/tls -> tcp(socket)

最后来一张wireshark的抓包,很明显看到从tcp的三次握手开始, 紧接着tls的四次捂手,然后就是真正的请求数据的传输了…

IMAGE

reference

#macos security