winlin

support multiple http hooks for a event.

1 -/*  
2 -The MIT License (MIT)  
3 -  
4 -Copyright (c) 2013 winlin  
5 -  
6 -Permission is hereby granted, free of charge, to any person obtaining a copy of  
7 -this software and associated documentation files (the "Software"), to deal in  
8 -the Software without restriction, including without limitation the rights to  
9 -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of  
10 -the Software, and to permit persons to whom the Software is furnished to do so,  
11 -subject to the following conditions:  
12 -  
13 -The above copyright notice and this permission notice shall be included in all  
14 -copies or substantial portions of the Software.  
15 -  
16 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
17 -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS  
18 -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR  
19 -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER  
20 -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN  
21 -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
22 -*/  
23 -  
24 -#include <srs_core_client.hpp>  
25 -  
26 -#include <arpa/inet.h>  
27 -#include <stdlib.h>  
28 -  
29 -#include <srs_core_error.hpp>  
30 -#include <srs_core_log.hpp>  
31 -#include <srs_core_rtmp.hpp>  
32 -#include <srs_core_protocol.hpp>  
33 -#include <srs_core_autofree.hpp>  
34 -#include <srs_core_source.hpp>  
35 -#include <srs_core_server.hpp>  
36 -#include <srs_core_pithy_print.hpp>  
37 -#include <srs_core_config.hpp>  
38 -#include <srs_core_refer.hpp>  
39 -#include <srs_core_hls.hpp>  
40 -#include <srs_core_http.hpp>  
41 -  
42 -#define SRS_PULSE_TIMEOUT_MS 100  
43 -#define SRS_SEND_TIMEOUT_US 5000000L  
44 -#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US  
45 -#define SRS_STREAM_BUSY_SLEEP_MS 2000  
46 -  
47 -SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)  
48 - : SrsConnection(srs_server, client_stfd)  
49 -{  
50 - ip = NULL;  
51 - req = new SrsRequest();  
52 - res = new SrsResponse();  
53 - rtmp = new SrsRtmp(client_stfd);  
54 - refer = new SrsRefer();  
55 -#ifdef SRS_HTTP  
56 - http_hooks = new SrsHttpHooks();  
57 -#endif  
58 -}  
59 -  
60 -SrsClient::~SrsClient()  
61 -{  
62 - srs_freepa(ip);  
63 - srs_freep(req);  
64 - srs_freep(res);  
65 - srs_freep(rtmp);  
66 - srs_freep(refer);  
67 -#ifdef SRS_HTTP  
68 - srs_freep(http_hooks);  
69 -#endif  
70 -}  
71 -  
72 -// TODO: return detail message when error for client.  
73 -int SrsClient::do_cycle()  
74 -{  
75 - int ret = ERROR_SUCCESS;  
76 -  
77 - if ((ret = get_peer_ip()) != ERROR_SUCCESS) {  
78 - srs_error("get peer ip failed. ret=%d", ret);  
79 - return ret;  
80 - }  
81 - srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"",  
82 - ip, SRS_SEND_TIMEOUT_US, SRS_RECV_TIMEOUT_US);  
83 -  
84 - rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US);  
85 - rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US);  
86 -  
87 - if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {  
88 - srs_error("rtmp handshake failed. ret=%d", ret);  
89 - return ret;  
90 - }  
91 - srs_verbose("rtmp handshake success");  
92 -  
93 - if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {  
94 - srs_error("rtmp connect vhost/app failed. ret=%d", ret);  
95 - return ret;  
96 - }  
97 - srs_verbose("rtmp connect app success");  
98 -  
99 - if ((ret = check_vhost()) != ERROR_SUCCESS) {  
100 - srs_error("check vhost failed. ret=%d", ret);  
101 - return ret;  
102 - }  
103 - srs_verbose("check vhost success.");  
104 -  
105 - srs_trace("rtmp connect app success. "  
106 - "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s",  
107 - req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),  
108 - req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),  
109 - req->app.c_str());  
110 -  
111 - if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) {  
112 - srs_error("check refer failed. ret=%d", ret);  
113 - return ret;  
114 - }  
115 - srs_verbose("check refer success.");  
116 -  
117 - if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) {  
118 - srs_error("set window acknowledgement size failed. ret=%d", ret);  
119 - return ret;  
120 - }  
121 - srs_verbose("set window acknowledgement size success");  
122 -  
123 - if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) {  
124 - srs_error("set peer bandwidth failed. ret=%d", ret);  
125 - return ret;  
126 - }  
127 - srs_verbose("set peer bandwidth success");  
128 -  
129 - if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) {  
130 - srs_error("response connect app failed. ret=%d", ret);  
131 - return ret;  
132 - }  
133 - srs_verbose("response connect app success");  
134 -  
135 - if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) {  
136 - srs_error("on_bw_done failed. ret=%d", ret);  
137 - return ret;  
138 - }  
139 - srs_verbose("on_bw_done success");  
140 -  
141 - SrsClientType type;  
142 - if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) {  
143 - srs_error("identify client failed. ret=%d", ret);  
144 - return ret;  
145 - }  
146 - req->strip();  
147 - srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str());  
148 -  
149 - int chunk_size = 4096;  
150 - SrsConfDirective* conf = config->get_chunk_size();  
151 - if (conf && !conf->arg0().empty()) {  
152 - chunk_size = ::atoi(conf->arg0().c_str());  
153 - }  
154 - if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) {  
155 - srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret);  
156 - return ret;  
157 - }  
158 - srs_trace("set chunk_size=%d success", chunk_size);  
159 -  
160 - // find a source to publish.  
161 - SrsSource* source = SrsSource::find(req->get_stream_url());  
162 - srs_assert(source != NULL);  
163 -  
164 - // check publish available.  
165 - if (type != SrsClientPlay && !source->can_publish()) {  
166 - ret = ERROR_SYSTEM_STREAM_BUSY;  
167 - srs_warn("stream %s is already publishing. ret=%d",  
168 - req->get_stream_url().c_str(), ret);  
169 - // to delay request  
170 - st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000);  
171 - return ret;  
172 - }  
173 -  
174 - bool enabled_cache = true;  
175 - conf = config->get_gop_cache(req->vhost);  
176 - if (conf && conf->arg0() == "off") {  
177 - enabled_cache = false;  
178 - }  
179 - source->set_cache(enabled_cache);  
180 -  
181 - srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache);  
182 -  
183 - switch (type) {  
184 - case SrsClientPlay: {  
185 - srs_verbose("start to play stream %s.", req->stream.c_str());  
186 -  
187 - if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) {  
188 - srs_error("start to play stream failed. ret=%d", ret);  
189 - return ret;  
190 - }  
191 - srs_info("start to play stream %s success", req->stream.c_str());  
192 - return playing(source);  
193 - }  
194 - case SrsClientFMLEPublish: {  
195 - srs_verbose("FMLE start to publish stream %s.", req->stream.c_str());  
196 -  
197 - if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) {  
198 - srs_error("start to publish stream failed. ret=%d", ret);  
199 - return ret;  
200 - }  
201 - srs_info("start to publish stream %s success", req->stream.c_str());  
202 - ret = publish(source, true);  
203 - source->on_unpublish();  
204 - return ret;  
205 - }  
206 - case SrsClientFlashPublish: {  
207 - srs_verbose("flash start to publish stream %s.", req->stream.c_str());  
208 -  
209 - if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) {  
210 - srs_error("flash start to publish stream failed. ret=%d", ret);  
211 - return ret;  
212 - }  
213 - srs_info("flash start to publish stream %s success", req->stream.c_str());  
214 - ret = publish(source, false);  
215 - source->on_unpublish();  
216 - return ret;  
217 - }  
218 - default: {  
219 - ret = ERROR_SYSTEM_CLIENT_INVALID;  
220 - srs_info("invalid client type=%d. ret=%d", type, ret);  
221 - return ret;  
222 - }  
223 - }  
224 -  
225 - return ret;  
226 -}  
227 -  
228 -int SrsClient::check_vhost()  
229 -{  
230 - int ret = ERROR_SUCCESS;  
231 -  
232 - srs_assert(req != NULL);  
233 -  
234 - SrsConfDirective* vhost = config->get_vhost(req->vhost);  
235 - if (vhost == NULL) {  
236 - ret = ERROR_RTMP_VHOST_NOT_FOUND;  
237 - srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret);  
238 - return ret;  
239 - }  
240 -  
241 - if (!config->get_vhost_enabled(req->vhost)) {  
242 - ret = ERROR_RTMP_VHOST_NOT_FOUND;  
243 - srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret);  
244 - return ret;  
245 - }  
246 -  
247 - if (req->vhost != vhost->arg0()) {  
248 - srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str());  
249 - req->vhost = vhost->arg0();  
250 - }  
251 -  
252 -#ifdef SRS_HTTP  
253 - // HTTP: on_connect  
254 - SrsConfDirective* on_connect = config->get_vhost_on_connect(req->vhost);  
255 - if (!on_connect) {  
256 - srs_info("ignore the empty http callback: on_connect");  
257 - return ret;  
258 - }  
259 -  
260 - for (int i = 0; i < (int)on_connect->args.size(); i++) {  
261 - std::string url = on_connect->args.at(i);  
262 - if ((ret = http_hooks->on_connect(url, ip, req)) != ERROR_SUCCESS) {  
263 - srs_error("hook client failed. url=%s, ret=%d", url.c_str(), ret);  
264 - return ret;  
265 - }  
266 - }  
267 -#endif  
268 -  
269 - return ret;  
270 -}  
271 -  
272 -int SrsClient::playing(SrsSource* source)  
273 -{  
274 - int ret = ERROR_SUCCESS;  
275 -  
276 - if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {  
277 - srs_error("check play_refer failed. ret=%d", ret);  
278 - return ret;  
279 - }  
280 - srs_verbose("check play_refer success.");  
281 -  
282 - SrsConsumer* consumer = NULL;  
283 - if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {  
284 - srs_error("create consumer failed. ret=%d", ret);  
285 - return ret;  
286 - }  
287 -  
288 - srs_assert(consumer != NULL);  
289 - SrsAutoFree(SrsConsumer, consumer, false);  
290 - srs_verbose("consumer created success.");  
291 -  
292 - rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000);  
293 -  
294 - SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER);  
295 -  
296 - while (true) {  
297 - pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);  
298 -  
299 - // switch to other st-threads.  
300 - st_usleep(0);  
301 -  
302 - // read from client.  
303 - int ctl_msg_ret = ERROR_SUCCESS;  
304 - if (true) {  
305 - SrsCommonMessage* msg = NULL;  
306 - ctl_msg_ret = ret = rtmp->recv_message(&msg);  
307 -  
308 - srs_verbose("play loop recv message. ret=%d", ret);  
309 - if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {  
310 - srs_error("recv client control message failed. ret=%d", ret);  
311 - return ret;  
312 - }  
313 - if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) {  
314 - srs_error("process play control message failed. ret=%d", ret);  
315 - return ret;  
316 - }  
317 - }  
318 -  
319 - // get messages from consumer.  
320 - SrsSharedPtrMessage** msgs = NULL;  
321 - int count = 0;  
322 - if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) {  
323 - srs_error("get messages from consumer failed. ret=%d", ret);  
324 - return ret;  
325 - }  
326 -  
327 - // reportable  
328 - if (pithy_print.can_print()) {  
329 - srs_trace("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",  
330 - pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());  
331 - }  
332 -  
333 - if (count <= 0) {  
334 - srs_verbose("no packets in queue.");  
335 - continue;  
336 - }  
337 - SrsAutoFree(SrsSharedPtrMessage*, msgs, true);  
338 -  
339 - // sendout messages  
340 - for (int i = 0; i < count; i++) {  
341 - SrsSharedPtrMessage* msg = msgs[i];  
342 -  
343 - // the send_message will free the msg,  
344 - // so set the msgs[i] to NULL.  
345 - msgs[i] = NULL;  
346 -  
347 - if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {  
348 - srs_error("send message to client failed. ret=%d", ret);  
349 - return ret;  
350 - }  
351 - }  
352 - }  
353 -  
354 - return ret;  
355 -}  
356 -  
357 -int SrsClient::publish(SrsSource* source, bool is_fmle)  
358 -{  
359 - int ret = ERROR_SUCCESS;  
360 -  
361 - if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) {  
362 - srs_error("check publish_refer failed. ret=%d", ret);  
363 - return ret;  
364 - }  
365 - srs_verbose("check publish_refer success.");  
366 -  
367 - SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);  
368 -  
369 - // notify the hls to prepare when publish start.  
370 - if ((ret = source->on_publish(req)) != ERROR_SUCCESS) {  
371 - srs_error("hls on_publish failed. ret=%d", ret);  
372 - return ret;  
373 - }  
374 - srs_verbose("hls on_publish success.");  
375 -  
376 - while (true) {  
377 - // switch to other st-threads.  
378 - st_usleep(0);  
379 -  
380 - SrsCommonMessage* msg = NULL;  
381 - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {  
382 - srs_error("recv identify client message failed. ret=%d", ret);  
383 - return ret;  
384 - }  
385 -  
386 - SrsAutoFree(SrsCommonMessage, msg, false);  
387 -  
388 - pithy_print.set_age(msg->header.timestamp);  
389 -  
390 - // reportable  
391 - if (pithy_print.can_print()) {  
392 - srs_trace("<- time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",  
393 - pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());  
394 - }  
395 -  
396 - if ((ret = process_publish_message(source, msg, is_fmle)) != ERROR_SUCCESS) {  
397 - srs_error("process publish message failed. ret=%d", ret);  
398 - return ret;  
399 - }  
400 - }  
401 -  
402 - return ret;  
403 -}  
404 -  
405 -int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle)  
406 -{  
407 - int ret = ERROR_SUCCESS;  
408 -  
409 - // process audio packet  
410 - if (msg->header.is_audio()) {  
411 - if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) {  
412 - srs_error("source process audio message failed. ret=%d", ret);  
413 - return ret;  
414 - }  
415 - }  
416 - // process video packet  
417 - if (msg->header.is_video()) {  
418 - if ((ret = source->on_video(msg)) != ERROR_SUCCESS) {  
419 - srs_error("source process video message failed. ret=%d", ret);  
420 - return ret;  
421 - }  
422 - }  
423 -  
424 - // process onMetaData  
425 - if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {  
426 - if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {  
427 - srs_error("decode onMetaData message failed. ret=%d", ret);  
428 - return ret;  
429 - }  
430 -  
431 - SrsPacket* pkt = msg->get_packet();  
432 - if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) {  
433 - SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt);  
434 - if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) {  
435 - srs_error("source process onMetaData message failed. ret=%d", ret);  
436 - return ret;  
437 - }  
438 - srs_trace("process onMetaData message success.");  
439 - return ret;  
440 - }  
441 -  
442 - srs_trace("ignore AMF0/AMF3 data message.");  
443 - return ret;  
444 - }  
445 -  
446 - // process UnPublish event.  
447 - if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) {  
448 - if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {  
449 - srs_error("decode unpublish message failed. ret=%d", ret);  
450 - return ret;  
451 - }  
452 -  
453 - // flash unpublish.  
454 - if (!is_fmle) {  
455 - srs_trace("flash publish finished.");  
456 - return ret;  
457 - }  
458 -  
459 - SrsPacket* pkt = msg->get_packet();  
460 - if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {  
461 - SrsFMLEStartPacket* unpublish = dynamic_cast<SrsFMLEStartPacket*>(pkt);  
462 - return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id);  
463 - }  
464 -  
465 - srs_trace("ignore AMF0/AMF3 command message.");  
466 - return ret;  
467 - }  
468 -  
469 - return ret;  
470 -}  
471 -  
472 -int SrsClient::get_peer_ip()  
473 -{  
474 - int ret = ERROR_SUCCESS;  
475 -  
476 - int fd = st_netfd_fileno(stfd);  
477 -  
478 - // discovery client information  
479 - sockaddr_in addr;  
480 - socklen_t addrlen = sizeof(addr);  
481 - if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) {  
482 - ret = ERROR_SOCKET_GET_PEER_NAME;  
483 - srs_error("discovery client information failed. ret=%d", ret);  
484 - return ret;  
485 - }  
486 - srs_verbose("get peer name success.");  
487 -  
488 - // ip v4 or v6  
489 - char buf[INET6_ADDRSTRLEN];  
490 - memset(buf, 0, sizeof(buf));  
491 -  
492 - if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) {  
493 - ret = ERROR_SOCKET_GET_PEER_IP;  
494 - srs_error("convert client information failed. ret=%d", ret);  
495 - return ret;  
496 - }  
497 - srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd);  
498 -  
499 - ip = new char[strlen(buf) + 1];  
500 - strcpy(ip, buf);  
501 -  
502 - srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd);  
503 -  
504 - return ret;  
505 -}  
506 -  
507 -int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg)  
508 -{  
509 - int ret = ERROR_SUCCESS;  
510 -  
511 - if (!msg) {  
512 - srs_verbose("ignore all empty message.");  
513 - return ret;  
514 - }  
515 - SrsAutoFree(SrsCommonMessage, msg, false);  
516 -  
517 - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {  
518 - srs_info("ignore all message except amf0/amf3 command.");  
519 - return ret;  
520 - }  
521 -  
522 - if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {  
523 - srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret);  
524 - return ret;  
525 - }  
526 - srs_info("decode the amf0/amf3 command packet success.");  
527 -  
528 - SrsPausePacket* pause = dynamic_cast<SrsPausePacket*>(msg->get_packet());  
529 - if (!pause) {  
530 - srs_info("ignore all amf0/amf3 command except pause.");  
531 - return ret;  
532 - }  
533 -  
534 - if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) {  
535 - srs_error("rtmp process play client pause failed. ret=%d", ret);  
536 - return ret;  
537 - }  
538 -  
539 - if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) {  
540 - srs_error("consumer process play client pause failed. ret=%d", ret);  
541 - return ret;  
542 - }  
543 - srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms);  
544 -  
545 - return ret;  
546 -}  
547 - 1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013 winlin
  5 +
  6 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 +this software and associated documentation files (the "Software"), to deal in
  8 +the Software without restriction, including without limitation the rights to
  9 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 +the Software, and to permit persons to whom the Software is furnished to do so,
  11 +subject to the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be included in all
  14 +copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +
  24 +#include <srs_core_client.hpp>
  25 +
  26 +#include <arpa/inet.h>
  27 +#include <stdlib.h>
  28 +
  29 +#include <srs_core_error.hpp>
  30 +#include <srs_core_log.hpp>
  31 +#include <srs_core_rtmp.hpp>
  32 +#include <srs_core_protocol.hpp>
  33 +#include <srs_core_autofree.hpp>
  34 +#include <srs_core_source.hpp>
  35 +#include <srs_core_server.hpp>
  36 +#include <srs_core_pithy_print.hpp>
  37 +#include <srs_core_config.hpp>
  38 +#include <srs_core_refer.hpp>
  39 +#include <srs_core_hls.hpp>
  40 +#include <srs_core_http.hpp>
  41 +
  42 +#define SRS_PULSE_TIMEOUT_MS 100
  43 +#define SRS_SEND_TIMEOUT_US 5000000L
  44 +#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
  45 +#define SRS_STREAM_BUSY_SLEEP_MS 2000
  46 +
  47 +SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
  48 + : SrsConnection(srs_server, client_stfd)
  49 +{
  50 + ip = NULL;
  51 + req = new SrsRequest();
  52 + res = new SrsResponse();
  53 + rtmp = new SrsRtmp(client_stfd);
  54 + refer = new SrsRefer();
  55 +#ifdef SRS_HTTP
  56 + http_hooks = new SrsHttpHooks();
  57 +#endif
  58 +}
  59 +
  60 +SrsClient::~SrsClient()
  61 +{
  62 + srs_freepa(ip);
  63 + srs_freep(req);
  64 + srs_freep(res);
  65 + srs_freep(rtmp);
  66 + srs_freep(refer);
  67 +#ifdef SRS_HTTP
  68 + srs_freep(http_hooks);
  69 +#endif
  70 +}
  71 +
  72 +// TODO: return detail message when error for client.
  73 +int SrsClient::do_cycle()
  74 +{
  75 + int ret = ERROR_SUCCESS;
  76 +
  77 + if ((ret = get_peer_ip()) != ERROR_SUCCESS) {
  78 + srs_error("get peer ip failed. ret=%d", ret);
  79 + return ret;
  80 + }
  81 + srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"",
  82 + ip, SRS_SEND_TIMEOUT_US, SRS_RECV_TIMEOUT_US);
  83 +
  84 + rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US);
  85 + rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US);
  86 +
  87 + if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
  88 + srs_error("rtmp handshake failed. ret=%d", ret);
  89 + return ret;
  90 + }
  91 + srs_verbose("rtmp handshake success");
  92 +
  93 + if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {
  94 + srs_error("rtmp connect vhost/app failed. ret=%d", ret);
  95 + return ret;
  96 + }
  97 + srs_verbose("rtmp connect app success");
  98 +
  99 + if ((ret = check_vhost()) != ERROR_SUCCESS) {
  100 + srs_error("check vhost failed. ret=%d", ret);
  101 + return ret;
  102 + }
  103 + srs_verbose("check vhost success.");
  104 +
  105 + srs_trace("rtmp connect app success. "
  106 + "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s",
  107 + req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
  108 + req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
  109 + req->app.c_str());
  110 +
  111 + if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) {
  112 + srs_error("check refer failed. ret=%d", ret);
  113 + return ret;
  114 + }
  115 + srs_verbose("check refer success.");
  116 +
  117 + if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) {
  118 + srs_error("set window acknowledgement size failed. ret=%d", ret);
  119 + return ret;
  120 + }
  121 + srs_verbose("set window acknowledgement size success");
  122 +
  123 + if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) {
  124 + srs_error("set peer bandwidth failed. ret=%d", ret);
  125 + return ret;
  126 + }
  127 + srs_verbose("set peer bandwidth success");
  128 +
  129 + if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) {
  130 + srs_error("response connect app failed. ret=%d", ret);
  131 + return ret;
  132 + }
  133 + srs_verbose("response connect app success");
  134 +
  135 + if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) {
  136 + srs_error("on_bw_done failed. ret=%d", ret);
  137 + return ret;
  138 + }
  139 + srs_verbose("on_bw_done success");
  140 +
  141 + SrsClientType type;
  142 + if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) {
  143 + srs_error("identify client failed. ret=%d", ret);
  144 + return ret;
  145 + }
  146 + req->strip();
  147 + srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str());
  148 +
  149 + int chunk_size = 4096;
  150 + SrsConfDirective* conf = config->get_chunk_size();
  151 + if (conf && !conf->arg0().empty()) {
  152 + chunk_size = ::atoi(conf->arg0().c_str());
  153 + }
  154 + if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) {
  155 + srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret);
  156 + return ret;
  157 + }
  158 + srs_trace("set chunk_size=%d success", chunk_size);
  159 +
  160 + // find a source to publish.
  161 + SrsSource* source = SrsSource::find(req->get_stream_url());
  162 + srs_assert(source != NULL);
  163 +
  164 + // check publish available.
  165 + if (type != SrsClientPlay && !source->can_publish()) {
  166 + ret = ERROR_SYSTEM_STREAM_BUSY;
  167 + srs_warn("stream %s is already publishing. ret=%d",
  168 + req->get_stream_url().c_str(), ret);
  169 + // to delay request
  170 + st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000);
  171 + return ret;
  172 + }
  173 +
  174 + bool enabled_cache = true;
  175 + conf = config->get_gop_cache(req->vhost);
  176 + if (conf && conf->arg0() == "off") {
  177 + enabled_cache = false;
  178 + }
  179 + source->set_cache(enabled_cache);
  180 +
  181 + srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache);
  182 +
  183 + switch (type) {
  184 + case SrsClientPlay: {
  185 + srs_verbose("start to play stream %s.", req->stream.c_str());
  186 +
  187 + if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) {
  188 + srs_error("start to play stream failed. ret=%d", ret);
  189 + return ret;
  190 + }
  191 + srs_info("start to play stream %s success", req->stream.c_str());
  192 + return playing(source);
  193 + }
  194 + case SrsClientFMLEPublish: {
  195 + srs_verbose("FMLE start to publish stream %s.", req->stream.c_str());
  196 +
  197 + if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) {
  198 + srs_error("start to publish stream failed. ret=%d", ret);
  199 + return ret;
  200 + }
  201 + srs_info("start to publish stream %s success", req->stream.c_str());
  202 + ret = publish(source, true);
  203 + source->on_unpublish();
  204 + return ret;
  205 + }
  206 + case SrsClientFlashPublish: {
  207 + srs_verbose("flash start to publish stream %s.", req->stream.c_str());
  208 +
  209 + if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) {
  210 + srs_error("flash start to publish stream failed. ret=%d", ret);
  211 + return ret;
  212 + }
  213 + srs_info("flash start to publish stream %s success", req->stream.c_str());
  214 + ret = publish(source, false);
  215 + source->on_unpublish();
  216 + return ret;
  217 + }
  218 + default: {
  219 + ret = ERROR_SYSTEM_CLIENT_INVALID;
  220 + srs_info("invalid client type=%d. ret=%d", type, ret);
  221 + return ret;
  222 + }
  223 + }
  224 +
  225 + return ret;
  226 +}
  227 +
  228 +int SrsClient::check_vhost()
  229 +{
  230 + int ret = ERROR_SUCCESS;
  231 +
  232 + srs_assert(req != NULL);
  233 +
  234 + SrsConfDirective* vhost = config->get_vhost(req->vhost);
  235 + if (vhost == NULL) {
  236 + ret = ERROR_RTMP_VHOST_NOT_FOUND;
  237 + srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret);
  238 + return ret;
  239 + }
  240 +
  241 + if (!config->get_vhost_enabled(req->vhost)) {
  242 + ret = ERROR_RTMP_VHOST_NOT_FOUND;
  243 + srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret);
  244 + return ret;
  245 + }
  246 +
  247 + if (req->vhost != vhost->arg0()) {
  248 + srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str());
  249 + req->vhost = vhost->arg0();
  250 + }
  251 +
  252 +#ifdef SRS_HTTP
  253 + // HTTP: on_connect
  254 + SrsConfDirective* on_connect = config->get_vhost_on_connect(req->vhost);
  255 + if (!on_connect) {
  256 + srs_info("ignore the empty http callback: on_connect");
  257 + return ret;
  258 + }
  259 +
  260 + for (int i = 0; i < (int)on_connect->args.size(); i++) {
  261 + std::string url = on_connect->args.at(i);
  262 + if ((ret = http_hooks->on_connect(url, ip, req)) != ERROR_SUCCESS) {
  263 + srs_error("hook client failed. url=%s, ret=%d", url.c_str(), ret);
  264 + return ret;
  265 + }
  266 + }
  267 +#endif
  268 +
  269 + return ret;
  270 +}
  271 +
  272 +int SrsClient::playing(SrsSource* source)
  273 +{
  274 + int ret = ERROR_SUCCESS;
  275 +
  276 + if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {
  277 + srs_error("check play_refer failed. ret=%d", ret);
  278 + return ret;
  279 + }
  280 + srs_verbose("check play_refer success.");
  281 +
  282 + SrsConsumer* consumer = NULL;
  283 + if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {
  284 + srs_error("create consumer failed. ret=%d", ret);
  285 + return ret;
  286 + }
  287 +
  288 + srs_assert(consumer != NULL);
  289 + SrsAutoFree(SrsConsumer, consumer, false);
  290 + srs_verbose("consumer created success.");
  291 +
  292 + rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000);
  293 +
  294 + SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER);
  295 +
  296 + while (true) {
  297 + pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
  298 +
  299 + // switch to other st-threads.
  300 + st_usleep(0);
  301 +
  302 + // read from client.
  303 + int ctl_msg_ret = ERROR_SUCCESS;
  304 + if (true) {
  305 + SrsCommonMessage* msg = NULL;
  306 + ctl_msg_ret = ret = rtmp->recv_message(&msg);
  307 +
  308 + srs_verbose("play loop recv message. ret=%d", ret);
  309 + if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
  310 + srs_error("recv client control message failed. ret=%d", ret);
  311 + return ret;
  312 + }
  313 + if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) {
  314 + srs_error("process play control message failed. ret=%d", ret);
  315 + return ret;
  316 + }
  317 + }
  318 +
  319 + // get messages from consumer.
  320 + SrsSharedPtrMessage** msgs = NULL;
  321 + int count = 0;
  322 + if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) {
  323 + srs_error("get messages from consumer failed. ret=%d", ret);
  324 + return ret;
  325 + }
  326 +
  327 + // reportable
  328 + if (pithy_print.can_print()) {
  329 + srs_trace("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
  330 + pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
  331 + }
  332 +
  333 + if (count <= 0) {
  334 + srs_verbose("no packets in queue.");
  335 + continue;
  336 + }
  337 + SrsAutoFree(SrsSharedPtrMessage*, msgs, true);
  338 +
  339 + // sendout messages
  340 + for (int i = 0; i < count; i++) {
  341 + SrsSharedPtrMessage* msg = msgs[i];
  342 +
  343 + // the send_message will free the msg,
  344 + // so set the msgs[i] to NULL.
  345 + msgs[i] = NULL;
  346 +
  347 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  348 + srs_error("send message to client failed. ret=%d", ret);
  349 + return ret;
  350 + }
  351 + }
  352 + }
  353 +
  354 + return ret;
  355 +}
  356 +
  357 +int SrsClient::publish(SrsSource* source, bool is_fmle)
  358 +{
  359 + int ret = ERROR_SUCCESS;
  360 +
  361 + if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) {
  362 + srs_error("check publish_refer failed. ret=%d", ret);
  363 + return ret;
  364 + }
  365 + srs_verbose("check publish_refer success.");
  366 +
  367 + SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);
  368 +
  369 + // notify the hls to prepare when publish start.
  370 + if ((ret = source->on_publish(req)) != ERROR_SUCCESS) {
  371 + srs_error("hls on_publish failed. ret=%d", ret);
  372 + return ret;
  373 + }
  374 + srs_verbose("hls on_publish success.");
  375 +
  376 + while (true) {
  377 + // switch to other st-threads.
  378 + st_usleep(0);
  379 +
  380 + SrsCommonMessage* msg = NULL;
  381 + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {
  382 + srs_error("recv identify client message failed. ret=%d", ret);
  383 + return ret;
  384 + }
  385 +
  386 + SrsAutoFree(SrsCommonMessage, msg, false);
  387 +
  388 + pithy_print.set_age(msg->header.timestamp);
  389 +
  390 + // reportable
  391 + if (pithy_print.can_print()) {
  392 + srs_trace("<- time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
  393 + pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
  394 + }
  395 +
  396 + if ((ret = process_publish_message(source, msg, is_fmle)) != ERROR_SUCCESS) {
  397 + srs_error("process publish message failed. ret=%d", ret);
  398 + return ret;
  399 + }
  400 + }
  401 +
  402 + return ret;
  403 +}
  404 +
  405 +int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle)
  406 +{
  407 + int ret = ERROR_SUCCESS;
  408 +
  409 + // process audio packet
  410 + if (msg->header.is_audio()) {
  411 + if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) {
  412 + srs_error("source process audio message failed. ret=%d", ret);
  413 + return ret;
  414 + }
  415 + }
  416 + // process video packet
  417 + if (msg->header.is_video()) {
  418 + if ((ret = source->on_video(msg)) != ERROR_SUCCESS) {
  419 + srs_error("source process video message failed. ret=%d", ret);
  420 + return ret;
  421 + }
  422 + }
  423 +
  424 + // process onMetaData
  425 + if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
  426 + if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
  427 + srs_error("decode onMetaData message failed. ret=%d", ret);
  428 + return ret;
  429 + }
  430 +
  431 + SrsPacket* pkt = msg->get_packet();
  432 + if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) {
  433 + SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt);
  434 + if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) {
  435 + srs_error("source process onMetaData message failed. ret=%d", ret);
  436 + return ret;
  437 + }
  438 + srs_trace("process onMetaData message success.");
  439 + return ret;
  440 + }
  441 +
  442 + srs_trace("ignore AMF0/AMF3 data message.");
  443 + return ret;
  444 + }
  445 +
  446 + // process UnPublish event.
  447 + if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) {
  448 + if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
  449 + srs_error("decode unpublish message failed. ret=%d", ret);
  450 + return ret;
  451 + }
  452 +
  453 + // flash unpublish.
  454 + if (!is_fmle) {
  455 + srs_trace("flash publish finished.");
  456 + return ret;
  457 + }
  458 +
  459 + SrsPacket* pkt = msg->get_packet();
  460 + if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {
  461 + SrsFMLEStartPacket* unpublish = dynamic_cast<SrsFMLEStartPacket*>(pkt);
  462 + return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id);
  463 + }
  464 +
  465 + srs_trace("ignore AMF0/AMF3 command message.");
  466 + return ret;
  467 + }
  468 +
  469 + return ret;
  470 +}
  471 +
  472 +int SrsClient::get_peer_ip()
  473 +{
  474 + int ret = ERROR_SUCCESS;
  475 +
  476 + int fd = st_netfd_fileno(stfd);
  477 +
  478 + // discovery client information
  479 + sockaddr_in addr;
  480 + socklen_t addrlen = sizeof(addr);
  481 + if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) {
  482 + ret = ERROR_SOCKET_GET_PEER_NAME;
  483 + srs_error("discovery client information failed. ret=%d", ret);
  484 + return ret;
  485 + }
  486 + srs_verbose("get peer name success.");
  487 +
  488 + // ip v4 or v6
  489 + char buf[INET6_ADDRSTRLEN];
  490 + memset(buf, 0, sizeof(buf));
  491 +
  492 + if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) {
  493 + ret = ERROR_SOCKET_GET_PEER_IP;
  494 + srs_error("convert client information failed. ret=%d", ret);
  495 + return ret;
  496 + }
  497 + srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd);
  498 +
  499 + ip = new char[strlen(buf) + 1];
  500 + strcpy(ip, buf);
  501 +
  502 + srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd);
  503 +
  504 + return ret;
  505 +}
  506 +
  507 +int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg)
  508 +{
  509 + int ret = ERROR_SUCCESS;
  510 +
  511 + if (!msg) {
  512 + srs_verbose("ignore all empty message.");
  513 + return ret;
  514 + }
  515 + SrsAutoFree(SrsCommonMessage, msg, false);
  516 +
  517 + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
  518 + srs_info("ignore all message except amf0/amf3 command.");
  519 + return ret;
  520 + }
  521 +
  522 + if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) {
  523 + srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret);
  524 + return ret;
  525 + }
  526 + srs_info("decode the amf0/amf3 command packet success.");
  527 +
  528 + SrsPausePacket* pause = dynamic_cast<SrsPausePacket*>(msg->get_packet());
  529 + if (!pause) {
  530 + srs_info("ignore all amf0/amf3 command except pause.");
  531 + return ret;
  532 + }
  533 +
  534 + if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) {
  535 + srs_error("rtmp process play client pause failed. ret=%d", ret);
  536 + return ret;
  537 + }
  538 +
  539 + if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) {
  540 + srs_error("consumer process play client pause failed. ret=%d", ret);
  541 + return ret;
  542 + }
  543 + srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms);
  544 +
  545 + return ret;
  546 +}
  547 +
1 -/*  
2 -The MIT License (MIT)  
3 -  
4 -Copyright (c) 2013 winlin  
5 -  
6 -Permission is hereby granted, free of charge, to any person obtaining a copy of  
7 -this software and associated documentation files (the "Software"), to deal in  
8 -the Software without restriction, including without limitation the rights to  
9 -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of  
10 -the Software, and to permit persons to whom the Software is furnished to do so,  
11 -subject to the following conditions:  
12 -  
13 -The above copyright notice and this permission notice shall be included in all  
14 -copies or substantial portions of the Software.  
15 -  
16 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
17 -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS  
18 -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR  
19 -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER  
20 -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN  
21 -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
22 -*/  
23 -  
24 -#include <srs_core_config.hpp>  
25 -  
26 -#include <stdio.h>  
27 -#include <stdlib.h>  
28 -#include <errno.h>  
29 -#include <string.h>  
30 -// file operations.  
31 -#include <unistd.h>  
32 -#include <sys/types.h>  
33 -#include <sys/stat.h>  
34 -#include <fcntl.h>  
35 -  
36 -#include <vector>  
37 -#include <algorithm>  
38 -  
39 -#include <srs_core_error.hpp>  
40 -#include <srs_core_log.hpp>  
41 -#include <srs_core_autofree.hpp>  
42 -  
43 -#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)  
44 -  
45 -int64_t FILE_SIZE(int fd)  
46 -{  
47 - int64_t pre = FILE_OFFSET(fd);  
48 - int64_t pos = lseek(fd, 0, SEEK_END);  
49 - lseek(fd, pre, SEEK_SET);  
50 - return pos;  
51 -}  
52 -  
53 -#define LF (char)0x0a  
54 -#define CR (char)0x0d  
55 -  
56 -bool is_common_space(char ch)  
57 -{  
58 - return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);  
59 -}  
60 -  
61 -#define CONF_BUFFER_SIZE 1024 * 1024  
62 -  
63 -SrsFileBuffer::SrsFileBuffer()  
64 -{  
65 - fd = -1;  
66 - line = 0;  
67 -  
68 - pos = last = start = new char[CONF_BUFFER_SIZE];  
69 - end = start + CONF_BUFFER_SIZE;  
70 -}  
71 -  
72 -SrsFileBuffer::~SrsFileBuffer()  
73 -{  
74 - if (fd > 0) {  
75 - close(fd);  
76 - }  
77 - srs_freepa(start);  
78 -}  
79 -  
80 -int SrsFileBuffer::open(const char* filename)  
81 -{  
82 - assert(fd == -1);  
83 -  
84 - if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {  
85 - srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno));  
86 - return ERROR_SYSTEM_CONFIG_INVALID;  
87 - }  
88 -  
89 - line = 1;  
90 -  
91 - return ERROR_SUCCESS;  
92 -}  
93 -  
94 -SrsConfDirective::SrsConfDirective()  
95 -{  
96 -}  
97 -  
98 -SrsConfDirective::~SrsConfDirective()  
99 -{  
100 - std::vector<SrsConfDirective*>::iterator it;  
101 - for (it = directives.begin(); it != directives.end(); ++it) {  
102 - SrsConfDirective* directive = *it;  
103 - srs_freep(directive);  
104 - }  
105 - directives.clear();  
106 -}  
107 -  
108 -std::string SrsConfDirective::arg0()  
109 -{  
110 - if (args.size() > 0) {  
111 - return args.at(0);  
112 - }  
113 -  
114 - return "";  
115 -}  
116 -  
117 -std::string SrsConfDirective::arg1()  
118 -{  
119 - if (args.size() > 1) {  
120 - return args.at(1);  
121 - }  
122 -  
123 - return "";  
124 -}  
125 -  
126 -std::string SrsConfDirective::arg2()  
127 -{  
128 - if (args.size() > 2) {  
129 - return args.at(2);  
130 - }  
131 -  
132 - return "";  
133 -}  
134 -  
135 -SrsConfDirective* SrsConfDirective::at(int index)  
136 -{  
137 - return directives.at(index);  
138 -}  
139 -  
140 -SrsConfDirective* SrsConfDirective::get(std::string _name)  
141 -{  
142 - std::vector<SrsConfDirective*>::iterator it;  
143 - for (it = directives.begin(); it != directives.end(); ++it) {  
144 - SrsConfDirective* directive = *it;  
145 - if (directive->name == _name) {  
146 - return directive;  
147 - }  
148 - }  
149 -  
150 - return NULL;  
151 -}  
152 -  
153 -int SrsConfDirective::parse(const char* filename)  
154 -{  
155 - int ret = ERROR_SUCCESS;  
156 -  
157 - SrsFileBuffer buffer;  
158 -  
159 - if ((ret = buffer.open(filename)) != ERROR_SUCCESS) {  
160 - return ret;  
161 - }  
162 -  
163 - return parse_conf(&buffer, parse_file);  
164 -}  
165 -  
166 -// see: ngx_conf_parse  
167 -int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)  
168 -{  
169 - int ret = ERROR_SUCCESS;  
170 -  
171 - while (true) {  
172 - std::vector<std::string> args;  
173 - ret = read_token(buffer, args);  
174 -  
175 - /**  
176 - * ret maybe:  
177 - * ERROR_SYSTEM_CONFIG_INVALID error.  
178 - * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found  
179 - * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found  
180 - * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found  
181 - * ERROR_SYSTEM_CONFIG_EOF the config file is done  
182 - */  
183 - if (ret == ERROR_SYSTEM_CONFIG_INVALID) {  
184 - return ret;  
185 - }  
186 - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {  
187 - if (type != parse_block) {  
188 - srs_error("line %d: unexpected \"}\"", buffer->line);  
189 - return ret;  
190 - }  
191 - return ERROR_SUCCESS;  
192 - }  
193 - if (ret == ERROR_SYSTEM_CONFIG_EOF) {  
194 - if (type == parse_block) {  
195 - srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);  
196 - return ret;  
197 - }  
198 - return ERROR_SUCCESS;  
199 - }  
200 -  
201 - if (args.empty()) {  
202 - srs_error("line %d: empty directive.", buffer->line);  
203 - return ret;  
204 - }  
205 -  
206 - // build directive tree.  
207 - SrsConfDirective* directive = new SrsConfDirective();  
208 -  
209 - directive->conf_line = buffer->line;  
210 - directive->name = args[0];  
211 - args.erase(args.begin());  
212 - directive->args.swap(args);  
213 -  
214 - directives.push_back(directive);  
215 -  
216 - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {  
217 - if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {  
218 - return ret;  
219 - }  
220 - }  
221 - }  
222 -  
223 - return ret;  
224 -}  
225 -  
226 -// see: ngx_conf_read_token  
227 -int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<std::string>& args)  
228 -{  
229 - int ret = ERROR_SUCCESS;  
230 -  
231 - char* pstart = buffer->pos;  
232 - int startline = buffer->line;  
233 -  
234 - bool sharp_comment = false;  
235 -  
236 - bool d_quoted = false;  
237 - bool s_quoted = false;  
238 -  
239 - bool need_space = false;  
240 - bool last_space = true;  
241 -  
242 - while (true) {  
243 - if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) {  
244 - if (!args.empty() || !last_space) {  
245 - srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);  
246 - return ERROR_SYSTEM_CONFIG_INVALID;  
247 - }  
248 - return ret;  
249 - }  
250 -  
251 - char ch = *buffer->pos++;  
252 -  
253 - if (ch == LF) {  
254 - buffer->line++;  
255 - sharp_comment = false;  
256 - }  
257 -  
258 - if (sharp_comment) {  
259 - continue;  
260 - }  
261 -  
262 - if (need_space) {  
263 - if (is_common_space(ch)) {  
264 - last_space = true;  
265 - need_space = false;  
266 - continue;  
267 - }  
268 - if (ch == ';') {  
269 - return ERROR_SYSTEM_CONFIG_DIRECTIVE;  
270 - }  
271 - if (ch == '{') {  
272 - return ERROR_SYSTEM_CONFIG_BLOCK_START;  
273 - }  
274 - srs_error("line %d: unexpected '%c'", buffer->line, ch);  
275 - return ERROR_SYSTEM_CONFIG_INVALID;  
276 - }  
277 -  
278 - // last charecter is space.  
279 - if (last_space) {  
280 - if (is_common_space(ch)) {  
281 - continue;  
282 - }  
283 - pstart = buffer->pos - 1;  
284 - startline = buffer->line;  
285 - switch (ch) {  
286 - case ';':  
287 - if (args.size() == 0) {  
288 - srs_error("line %d: unexpected ';'", buffer->line);  
289 - return ERROR_SYSTEM_CONFIG_INVALID;  
290 - }  
291 - return ERROR_SYSTEM_CONFIG_DIRECTIVE;  
292 - case '{':  
293 - if (args.size() == 0) {  
294 - srs_error("line %d: unexpected '{'", buffer->line);  
295 - return ERROR_SYSTEM_CONFIG_INVALID;  
296 - }  
297 - return ERROR_SYSTEM_CONFIG_BLOCK_START;  
298 - case '}':  
299 - if (args.size() != 0) {  
300 - srs_error("line %d: unexpected '}'", buffer->line);  
301 - return ERROR_SYSTEM_CONFIG_INVALID;  
302 - }  
303 - return ERROR_SYSTEM_CONFIG_BLOCK_END;  
304 - case '#':  
305 - sharp_comment = 1;  
306 - continue;  
307 - case '"':  
308 - pstart++;  
309 - d_quoted = true;  
310 - last_space = 0;  
311 - continue;  
312 - case '\'':  
313 - pstart++;  
314 - s_quoted = true;  
315 - last_space = 0;  
316 - continue;  
317 - default:  
318 - last_space = 0;  
319 - continue;  
320 - }  
321 - } else {  
322 - // last charecter is not space  
323 - bool found = false;  
324 - if (d_quoted) {  
325 - if (ch == '"') {  
326 - d_quoted = false;  
327 - need_space = true;  
328 - found = true;  
329 - }  
330 - } else if (s_quoted) {  
331 - if (ch == '\'') {  
332 - s_quoted = false;  
333 - need_space = true;  
334 - found = true;  
335 - }  
336 - } else if (is_common_space(ch) || ch == ';' || ch == '{') {  
337 - last_space = true;  
338 - found = 1;  
339 - }  
340 -  
341 - if (found) {  
342 - int len = buffer->pos - pstart;  
343 - char* word = new char[len];  
344 - memcpy(word, pstart, len);  
345 - word[len - 1] = 0;  
346 -  
347 - std::string word_str = word;  
348 - if (!word_str.empty()) {  
349 - args.push_back(word_str);  
350 - }  
351 - srs_freepa(word);  
352 -  
353 - if (ch == ';') {  
354 - return ERROR_SYSTEM_CONFIG_DIRECTIVE;  
355 - }  
356 - if (ch == '{') {  
357 - return ERROR_SYSTEM_CONFIG_BLOCK_START;  
358 - }  
359 - }  
360 - }  
361 - }  
362 -  
363 - return ret;  
364 -}  
365 -  
366 -int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart)  
367 -{  
368 - int ret = ERROR_SUCCESS;  
369 -  
370 - if (buffer->pos < buffer->last) {  
371 - return ret;  
372 - }  
373 -  
374 - int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd);  
375 - if (size > CONF_BUFFER_SIZE) {  
376 - ret = ERROR_SYSTEM_CONFIG_TOO_LARGE;  
377 - srs_error("config file too large, max=%d, actual=%d, ret=%d",  
378 - CONF_BUFFER_SIZE, size, ret);  
379 - return ret;  
380 - }  
381 -  
382 - if (size <= 0) {  
383 - return ERROR_SYSTEM_CONFIG_EOF;  
384 - }  
385 -  
386 - int len = buffer->pos - buffer->start;  
387 - if (len >= CONF_BUFFER_SIZE) {  
388 - buffer->line = startline;  
389 -  
390 - if (!d_quoted && !s_quoted) {  
391 - srs_error("line %d: too long parameter \"%*s...\" started",  
392 - buffer->line, 10, buffer->start);  
393 -  
394 - } else {  
395 - srs_error("line %d: too long parameter, "  
396 - "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\'');  
397 - }  
398 - return ERROR_SYSTEM_CONFIG_INVALID;  
399 - }  
400 -  
401 - if (len) {  
402 - memmove(buffer->start, pstart, len);  
403 - }  
404 -  
405 - size = srs_min(size, buffer->end - (buffer->start + len));  
406 - int n = read(buffer->fd, buffer->start + len, size);  
407 - if (n != size) {  
408 - srs_error("read file read error. expect %d, actual %d bytes.", size, n);  
409 - return ERROR_SYSTEM_CONFIG_INVALID;  
410 - }  
411 -  
412 - buffer->pos = buffer->start + len;  
413 - buffer->last = buffer->pos + n;  
414 - pstart = buffer->start;  
415 -  
416 - return ret;  
417 -}  
418 -  
419 -SrsConfig* config = new SrsConfig();  
420 -  
421 -SrsConfig::SrsConfig()  
422 -{  
423 - show_help = false;  
424 - show_version = false;  
425 -  
426 - root = new SrsConfDirective();  
427 - root->conf_line = 0;  
428 - root->name = "root";  
429 -}  
430 -  
431 -SrsConfig::~SrsConfig()  
432 -{  
433 - srs_freep(root);  
434 -}  
435 -  
436 -int SrsConfig::reload()  
437 -{  
438 - int ret = ERROR_SUCCESS;  
439 -  
440 - SrsConfig conf;  
441 - if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) {  
442 - srs_error("config reloader parse file failed. ret=%d", ret);  
443 - return ret;  
444 - }  
445 - srs_info("config reloader parse file success.");  
446 -  
447 - // store current root to old_root,  
448 - // and reap the root from conf to current root.  
449 - SrsConfDirective* old_root = root;  
450 - SrsAutoFree(SrsConfDirective, old_root, false);  
451 -  
452 - root = conf.root;  
453 - conf.root = NULL;  
454 -  
455 - // merge config.  
456 - std::vector<SrsReloadHandler*>::iterator it;  
457 -  
458 - // merge config: listen  
459 - if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {  
460 - for (it = subscribes.begin(); it != subscribes.end(); ++it) {  
461 - SrsReloadHandler* subscribe = *it;  
462 - if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {  
463 - srs_error("notify subscribes reload listen failed. ret=%d", ret);  
464 - return ret;  
465 - }  
466 - }  
467 - srs_trace("reload listen success.");  
468 - }  
469 - // merge config: pithy_print  
470 - if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) {  
471 - for (it = subscribes.begin(); it != subscribes.end(); ++it) {  
472 - SrsReloadHandler* subscribe = *it;  
473 - if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {  
474 - srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);  
475 - return ret;  
476 - }  
477 - }  
478 - srs_trace("reload pithy_print success.");  
479 - }  
480 -  
481 - // TODO: suppor reload hls/forward/ffmpeg/http  
482 -  
483 - return ret;  
484 -}  
485 -  
486 -void SrsConfig::subscribe(SrsReloadHandler* handler)  
487 -{  
488 - std::vector<SrsReloadHandler*>::iterator it;  
489 -  
490 - it = std::find(subscribes.begin(), subscribes.end(), handler);  
491 - if (it != subscribes.end()) {  
492 - return;  
493 - }  
494 -  
495 - subscribes.push_back(handler);  
496 -}  
497 -  
498 -void SrsConfig::unsubscribe(SrsReloadHandler* handler)  
499 -{  
500 - std::vector<SrsReloadHandler*>::iterator it;  
501 -  
502 - it = std::find(subscribes.begin(), subscribes.end(), handler);  
503 - if (it == subscribes.end()) {  
504 - return;  
505 - }  
506 -  
507 - subscribes.erase(it);  
508 -}  
509 -  
510 -// see: ngx_get_options  
511 -int SrsConfig::parse_options(int argc, char** argv)  
512 -{  
513 - int ret = ERROR_SUCCESS;  
514 -  
515 - for (int i = 1; i < argc; i++) {  
516 - if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) {  
517 - return ret;  
518 - }  
519 - }  
520 -  
521 - if (show_help) {  
522 - print_help(argv);  
523 - }  
524 -  
525 - if (show_version) {  
526 - printf("%s\n", RTMP_SIG_SRS_VERSION);  
527 - }  
528 -  
529 - if (show_help || show_version) {  
530 - exit(0);  
531 - }  
532 -  
533 - if (config_file.empty()) {  
534 - ret = ERROR_SYSTEM_CONFIG_INVALID;  
535 - srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret);  
536 - return ret;  
537 - }  
538 -  
539 - return parse_file(config_file.c_str());  
540 -}  
541 -  
542 -SrsConfDirective* SrsConfig::get_vhost(std::string vhost)  
543 -{  
544 - srs_assert(root);  
545 -  
546 - for (int i = 0; i < (int)root->directives.size(); i++) {  
547 - SrsConfDirective* conf = root->at(i);  
548 -  
549 - if (conf->name != "vhost") {  
550 - continue;  
551 - }  
552 -  
553 - if (conf->arg0() == vhost) {  
554 - return conf;  
555 - }  
556 - }  
557 -  
558 - if (vhost != RTMP_VHOST_DEFAULT) {  
559 - return get_vhost(RTMP_VHOST_DEFAULT);  
560 - }  
561 -  
562 - return NULL;  
563 -}  
564 -  
565 -SrsConfDirective* SrsConfig::get_vhost_on_connect(std::string vhost)  
566 -{  
567 - SrsConfDirective* conf = get_vhost(vhost);  
568 -  
569 - if (!conf) {  
570 - return NULL;  
571 - }  
572 -  
573 - conf = conf->get("http_hooks");  
574 - if (!conf) {  
575 - return NULL;  
576 - }  
577 -  
578 - return conf->get("on_connect");  
579 -}  
580 -  
581 -bool SrsConfig::get_vhost_enabled(std::string vhost)  
582 -{  
583 - SrsConfDirective* vhost_conf = get_vhost(vhost);  
584 -  
585 - if (!vhost_conf) {  
586 - return true;  
587 - }  
588 -  
589 - SrsConfDirective* conf = vhost_conf->get("enabled");  
590 - if (!conf) {  
591 - return true;  
592 - }  
593 -  
594 - if (conf->arg0() == "off") {  
595 - return false;  
596 - }  
597 -  
598 - return true;  
599 -}  
600 -  
601 -SrsConfDirective* SrsConfig::get_transcode(std::string vhost, std::string scope)  
602 -{  
603 - SrsConfDirective* conf = get_vhost(vhost);  
604 -  
605 - if (!conf) {  
606 - return NULL;  
607 - }  
608 -  
609 - SrsConfDirective* transcode = conf->get("transcode");  
610 - if (!transcode) {  
611 - return NULL;  
612 - }  
613 -  
614 - if (transcode->arg0() == scope) {  
615 - return transcode;  
616 - }  
617 -  
618 - return NULL;  
619 -}  
620 -  
621 -bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)  
622 -{  
623 - if (!transcode) {  
624 - return false;  
625 - }  
626 -  
627 - SrsConfDirective* conf = transcode->get("enabled");  
628 - if (!conf || conf->arg0() != "on") {  
629 - return false;  
630 - }  
631 -  
632 - return true;  
633 -}  
634 -  
635 -std::string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)  
636 -{  
637 - if (!transcode) {  
638 - return "";  
639 - }  
640 -  
641 - SrsConfDirective* conf = transcode->get("ffmpeg");  
642 - if (!conf) {  
643 - return "";  
644 - }  
645 -  
646 - return conf->arg0();  
647 -}  
648 -  
649 -void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines)  
650 -{  
651 - if (!transcode) {  
652 - return;  
653 - }  
654 -  
655 - for (int i = 0; i < (int)transcode->directives.size(); i++) {  
656 - SrsConfDirective* conf = transcode->directives[i];  
657 -  
658 - if (conf->name == "engine") {  
659 - engines.push_back(conf);  
660 - }  
661 - }  
662 -  
663 - return;  
664 -}  
665 -  
666 -bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)  
667 -{  
668 - if (!engine) {  
669 - return false;  
670 - }  
671 -  
672 - SrsConfDirective* conf = engine->get("enabled");  
673 - if (!conf || conf->arg0() != "on") {  
674 - return false;  
675 - }  
676 -  
677 - return true;  
678 -}  
679 -  
680 -std::string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)  
681 -{  
682 - if (!engine) {  
683 - return "";  
684 - }  
685 -  
686 - SrsConfDirective* conf = engine->get("vcodec");  
687 - if (!conf) {  
688 - return "";  
689 - }  
690 -  
691 - return conf->arg0();  
692 -}  
693 -  
694 -int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)  
695 -{  
696 - if (!engine) {  
697 - return 0;  
698 - }  
699 -  
700 - SrsConfDirective* conf = engine->get("vbitrate");  
701 - if (!conf) {  
702 - return 0;  
703 - }  
704 -  
705 - return ::atoi(conf->arg0().c_str());  
706 -}  
707 -  
708 -double SrsConfig::get_engine_vfps(SrsConfDirective* engine)  
709 -{  
710 - if (!engine) {  
711 - return 0;  
712 - }  
713 -  
714 - SrsConfDirective* conf = engine->get("vfps");  
715 - if (!conf) {  
716 - return 0;  
717 - }  
718 -  
719 - return ::atof(conf->arg0().c_str());  
720 -}  
721 -  
722 -int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)  
723 -{  
724 - if (!engine) {  
725 - return 0;  
726 - }  
727 -  
728 - SrsConfDirective* conf = engine->get("vwidth");  
729 - if (!conf) {  
730 - return 0;  
731 - }  
732 -  
733 - return ::atoi(conf->arg0().c_str());  
734 -}  
735 -  
736 -int SrsConfig::get_engine_vheight(SrsConfDirective* engine)  
737 -{  
738 - if (!engine) {  
739 - return 0;  
740 - }  
741 -  
742 - SrsConfDirective* conf = engine->get("vheight");  
743 - if (!conf) {  
744 - return 0;  
745 - }  
746 -  
747 - return ::atoi(conf->arg0().c_str());  
748 -}  
749 -  
750 -int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)  
751 -{  
752 - if (!engine) {  
753 - return 0;  
754 - }  
755 -  
756 - SrsConfDirective* conf = engine->get("vthreads");  
757 - if (!conf) {  
758 - return 0;  
759 - }  
760 -  
761 - return ::atoi(conf->arg0().c_str());  
762 -}  
763 -  
764 -std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)  
765 -{  
766 - if (!engine) {  
767 - return "";  
768 - }  
769 -  
770 - SrsConfDirective* conf = engine->get("vprofile");  
771 - if (!conf) {  
772 - return "";  
773 - }  
774 -  
775 - return conf->arg0();  
776 -}  
777 -  
778 -std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)  
779 -{  
780 - if (!engine) {  
781 - return "";  
782 - }  
783 -  
784 - SrsConfDirective* conf = engine->get("vpreset");  
785 - if (!conf) {  
786 - return "";  
787 - }  
788 -  
789 - return conf->arg0();  
790 -}  
791 -  
792 -void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams)  
793 -{  
794 - if (!engine) {  
795 - return;  
796 - }  
797 -  
798 - SrsConfDirective* conf = engine->get("vparams");  
799 - if (!conf) {  
800 - return;  
801 - }  
802 -  
803 - for (int i = 0; i < (int)conf->directives.size(); i++) {  
804 - SrsConfDirective* p = conf->directives[i];  
805 - if (!p) {  
806 - continue;  
807 - }  
808 -  
809 - vparams.push_back("-" + p->name);  
810 - vparams.push_back(p->arg0());  
811 - }  
812 -}  
813 -  
814 -void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter)  
815 -{  
816 - if (!engine) {  
817 - return;  
818 - }  
819 -  
820 - SrsConfDirective* conf = engine->get("vfilter");  
821 - if (!conf) {  
822 - return;  
823 - }  
824 -  
825 - for (int i = 0; i < (int)conf->directives.size(); i++) {  
826 - SrsConfDirective* p = conf->directives[i];  
827 - if (!p) {  
828 - continue;  
829 - }  
830 -  
831 - vfilter.push_back("-" + p->name);  
832 - vfilter.push_back(p->arg0());  
833 - }  
834 -}  
835 -  
836 -std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)  
837 -{  
838 - if (!engine) {  
839 - return "";  
840 - }  
841 -  
842 - SrsConfDirective* conf = engine->get("acodec");  
843 - if (!conf) {  
844 - return "";  
845 - }  
846 -  
847 - return conf->arg0();  
848 -}  
849 -  
850 -int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)  
851 -{  
852 - if (!engine) {  
853 - return 0;  
854 - }  
855 -  
856 - SrsConfDirective* conf = engine->get("abitrate");  
857 - if (!conf) {  
858 - return 0;  
859 - }  
860 -  
861 - return ::atoi(conf->arg0().c_str());  
862 -}  
863 -  
864 -int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)  
865 -{  
866 - if (!engine) {  
867 - return 0;  
868 - }  
869 -  
870 - SrsConfDirective* conf = engine->get("asample_rate");  
871 - if (!conf) {  
872 - return 0;  
873 - }  
874 -  
875 - return ::atoi(conf->arg0().c_str());  
876 -}  
877 -  
878 -int SrsConfig::get_engine_achannels(SrsConfDirective* engine)  
879 -{  
880 - if (!engine) {  
881 - return 0;  
882 - }  
883 -  
884 - SrsConfDirective* conf = engine->get("achannels");  
885 - if (!conf) {  
886 - return 0;  
887 - }  
888 -  
889 - return ::atoi(conf->arg0().c_str());  
890 -}  
891 -  
892 -void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams)  
893 -{  
894 - if (!engine) {  
895 - return;  
896 - }  
897 -  
898 - SrsConfDirective* conf = engine->get("aparams");  
899 - if (!conf) {  
900 - return;  
901 - }  
902 -  
903 - for (int i = 0; i < (int)conf->directives.size(); i++) {  
904 - SrsConfDirective* p = conf->directives[i];  
905 - if (!p) {  
906 - continue;  
907 - }  
908 -  
909 - aparams.push_back("-" + p->name);  
910 - aparams.push_back(p->arg0());  
911 - }  
912 -}  
913 -  
914 -std::string SrsConfig::get_engine_output(SrsConfDirective* engine)  
915 -{  
916 - if (!engine) {  
917 - return "";  
918 - }  
919 -  
920 - SrsConfDirective* conf = engine->get("output");  
921 - if (!conf) {  
922 - return "";  
923 - }  
924 -  
925 - return conf->arg0();  
926 -}  
927 -  
928 -std::string SrsConfig::get_log_dir()  
929 -{  
930 - srs_assert(root);  
931 -  
932 - SrsConfDirective* conf = root->get("log_dir");  
933 - if (!conf || conf->arg0().empty()) {  
934 - return "./objs/logs";  
935 - }  
936 -  
937 - return conf->arg0();  
938 -}  
939 -  
940 -int SrsConfig::get_max_connections()  
941 -{  
942 - srs_assert(root);  
943 -  
944 - SrsConfDirective* conf = root->get("max_connections");  
945 - if (!conf || conf->arg0().empty()) {  
946 - return 2000;  
947 - }  
948 -  
949 - return ::atoi(conf->arg0().c_str());  
950 -}  
951 -  
952 -SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost)  
953 -{  
954 - SrsConfDirective* conf = get_vhost(vhost);  
955 -  
956 - if (!conf) {  
957 - return NULL;  
958 - }  
959 -  
960 - return conf->get("gop_cache");  
961 -}  
962 -  
963 -SrsConfDirective* SrsConfig::get_forward(std::string vhost)  
964 -{  
965 - SrsConfDirective* conf = get_vhost(vhost);  
966 -  
967 - if (!conf) {  
968 - return NULL;  
969 - }  
970 -  
971 - return conf->get("forward");  
972 -}  
973 -  
974 -SrsConfDirective* SrsConfig::get_hls(std::string vhost)  
975 -{  
976 - SrsConfDirective* conf = get_vhost(vhost);  
977 -  
978 - if (!conf) {  
979 - return NULL;  
980 - }  
981 -  
982 - return conf->get("hls");  
983 -}  
984 -  
985 -bool SrsConfig::get_hls_enabled(std::string vhost)  
986 -{  
987 - SrsConfDirective* hls = get_hls(vhost);  
988 -  
989 - if (!hls) {  
990 - return true;  
991 - }  
992 -  
993 - if (hls->arg0() == "off") {  
994 - return false;  
995 - }  
996 -  
997 - return true;  
998 -}  
999 -  
1000 -SrsConfDirective* SrsConfig::get_hls_path(std::string vhost)  
1001 -{  
1002 - SrsConfDirective* conf = get_vhost(vhost);  
1003 -  
1004 - if (!conf) {  
1005 - return NULL;  
1006 - }  
1007 -  
1008 - return conf->get("hls_path");  
1009 -}  
1010 -  
1011 -SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost)  
1012 -{  
1013 - SrsConfDirective* conf = get_vhost(vhost);  
1014 -  
1015 - if (!conf) {  
1016 - return NULL;  
1017 - }  
1018 -  
1019 - return conf->get("hls_fragment");  
1020 -}  
1021 -  
1022 -SrsConfDirective* SrsConfig::get_hls_window(std::string vhost)  
1023 -{  
1024 - SrsConfDirective* conf = get_vhost(vhost);  
1025 -  
1026 - if (!conf) {  
1027 - return NULL;  
1028 - }  
1029 -  
1030 - return conf->get("hls_window");  
1031 -}  
1032 -  
1033 -SrsConfDirective* SrsConfig::get_refer(std::string vhost)  
1034 -{  
1035 - SrsConfDirective* conf = get_vhost(vhost);  
1036 -  
1037 - if (!conf) {  
1038 - return NULL;  
1039 - }  
1040 -  
1041 - return conf->get("refer");  
1042 -}  
1043 -  
1044 -SrsConfDirective* SrsConfig::get_refer_play(std::string vhost)  
1045 -{  
1046 - SrsConfDirective* conf = get_vhost(vhost);  
1047 -  
1048 - if (!conf) {  
1049 - return NULL;  
1050 - }  
1051 -  
1052 - return conf->get("refer_play");  
1053 -}  
1054 -  
1055 -SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost)  
1056 -{  
1057 - SrsConfDirective* conf = get_vhost(vhost);  
1058 -  
1059 - if (!conf) {  
1060 - return NULL;  
1061 - }  
1062 -  
1063 - return conf->get("refer_publish");  
1064 -}  
1065 -  
1066 -SrsConfDirective* SrsConfig::get_listen()  
1067 -{  
1068 - return root->get("listen");  
1069 -}  
1070 -  
1071 -SrsConfDirective* SrsConfig::get_chunk_size()  
1072 -{  
1073 - return root->get("chunk_size");  
1074 -}  
1075 -  
1076 -SrsConfDirective* SrsConfig::get_pithy_print_publish()  
1077 -{  
1078 - SrsConfDirective* pithy = root->get("pithy_print");  
1079 - if (!pithy) {  
1080 - return NULL;  
1081 - }  
1082 -  
1083 - return pithy->get("publish");  
1084 -}  
1085 -  
1086 -SrsConfDirective* SrsConfig::get_pithy_print_forwarder()  
1087 -{  
1088 - SrsConfDirective* pithy = root->get("pithy_print");  
1089 - if (!pithy) {  
1090 - return NULL;  
1091 - }  
1092 -  
1093 - return pithy->get("forwarder");  
1094 -}  
1095 -  
1096 -SrsConfDirective* SrsConfig::get_pithy_print_hls()  
1097 -{  
1098 - SrsConfDirective* pithy = root->get("pithy_print");  
1099 - if (!pithy) {  
1100 - return NULL;  
1101 - }  
1102 -  
1103 - return pithy->get("hls");  
1104 -}  
1105 -  
1106 -SrsConfDirective* SrsConfig::get_pithy_print_encoder()  
1107 -{  
1108 - SrsConfDirective* pithy = root->get("encoder");  
1109 - if (!pithy) {  
1110 - return NULL;  
1111 - }  
1112 -  
1113 - return pithy->get("forwarder");  
1114 -}  
1115 -  
1116 -SrsConfDirective* SrsConfig::get_pithy_print_play()  
1117 -{  
1118 - SrsConfDirective* pithy = root->get("pithy_print");  
1119 - if (!pithy) {  
1120 - return NULL;  
1121 - }  
1122 -  
1123 - return pithy->get("play");  
1124 -}  
1125 -  
1126 -int SrsConfig::parse_file(const char* filename)  
1127 -{  
1128 - int ret = ERROR_SUCCESS;  
1129 -  
1130 - config_file = filename;  
1131 -  
1132 - if (config_file.empty()) {  
1133 - return ERROR_SYSTEM_CONFIG_INVALID;  
1134 - }  
1135 -  
1136 - if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) {  
1137 - return ret;  
1138 - }  
1139 -  
1140 - SrsConfDirective* conf = NULL;  
1141 - if ((conf = get_listen()) == NULL || conf->args.size() == 0) {  
1142 - ret = ERROR_SYSTEM_CONFIG_INVALID;  
1143 - srs_error("line %d: conf error, "  
1144 - "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret);  
1145 - return ret;  
1146 - }  
1147 - // TODO: check the hls.  
1148 - // TODO: check other config.  
1149 - // TODO: check hls.  
1150 - // TODO: check ssl.  
1151 - // TODO: check ffmpeg.  
1152 - // TODO: check http.  
1153 -  
1154 - return ret;  
1155 -}  
1156 -  
1157 -int SrsConfig::parse_argv(int& i, char** argv)  
1158 -{  
1159 - int ret = ERROR_SUCCESS;  
1160 -  
1161 - char* p = argv[i];  
1162 -  
1163 - if (*p++ != '-') {  
1164 - ret = ERROR_SYSTEM_CONFIG_INVALID;  
1165 - srs_error("invalid options(index=%d, value=%s), "  
1166 - "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret);  
1167 - return ret;  
1168 - }  
1169 -  
1170 - while (*p) {  
1171 - switch (*p++) {  
1172 - case '?':  
1173 - case 'h':  
1174 - show_help = true;  
1175 - break;  
1176 - case 'v':  
1177 - case 'V':  
1178 - show_version = true;  
1179 - break;  
1180 - case 'c':  
1181 - if (*p) {  
1182 - config_file = p;  
1183 - return ret;  
1184 - }  
1185 - if (argv[++i]) {  
1186 - config_file = argv[i];  
1187 - return ret;  
1188 - }  
1189 - ret = ERROR_SYSTEM_CONFIG_INVALID;  
1190 - srs_error("option \"-c\" requires parameter, ret=%d", ret);  
1191 - return ret;  
1192 - default:  
1193 - ret = ERROR_SYSTEM_CONFIG_INVALID;  
1194 - srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret);  
1195 - return ret;  
1196 - }  
1197 - }  
1198 -  
1199 - return ret;  
1200 -}  
1201 -  
1202 -void SrsConfig::print_help(char** argv)  
1203 -{  
1204 - printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION  
1205 - " Copyright (c) 2013 winlin\n"  
1206 - "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n"  
1207 - "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n"  
1208 - "Usage: %s [-h?vV] [-c <filename>]\n"  
1209 - "\n"  
1210 - "Options:\n"  
1211 - " -?-h : show help\n"  
1212 - " -v-V : show version and exit\n"  
1213 - " -c filename : set configuration file\n"  
1214 - "\n"  
1215 - RTMP_SIG_SRS_WEB"\n"  
1216 - RTMP_SIG_SRS_URL"\n"  
1217 - "Email: "RTMP_SIG_SRS_EMAIL"\n"  
1218 - "\n",  
1219 - argv[0]);  
1220 -}  
1221 -  
1222 -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)  
1223 -{  
1224 - if (!a || !b) {  
1225 - return false;  
1226 - }  
1227 -  
1228 - if (a->name != b->name) {  
1229 - return false;  
1230 - }  
1231 -  
1232 - if (a->args.size() != b->args.size()) {  
1233 - return false;  
1234 - }  
1235 -  
1236 - for (int i = 0; i < (int)a->args.size(); i++) {  
1237 - if (a->args.at(i) != b->args.at(i)) {  
1238 - return false;  
1239 - }  
1240 - }  
1241 -  
1242 - if (a->directives.size() != b->directives.size()) {  
1243 - return false;  
1244 - }  
1245 -  
1246 - for (int i = 0; i < (int)a->directives.size(); i++) {  
1247 - SrsConfDirective* a0 = a->at(i);  
1248 - SrsConfDirective* b0 = b->at(i);  
1249 -  
1250 - if (!srs_directive_equals(a0, b0)) {  
1251 - return false;  
1252 - }  
1253 - }  
1254 -  
1255 - return true;  
1256 -}  
1257 - 1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013 winlin
  5 +
  6 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 +this software and associated documentation files (the "Software"), to deal in
  8 +the Software without restriction, including without limitation the rights to
  9 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 +the Software, and to permit persons to whom the Software is furnished to do so,
  11 +subject to the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be included in all
  14 +copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +
  24 +#include <srs_core_config.hpp>
  25 +
  26 +#include <stdio.h>
  27 +#include <stdlib.h>
  28 +#include <errno.h>
  29 +#include <string.h>
  30 +// file operations.
  31 +#include <unistd.h>
  32 +#include <sys/types.h>
  33 +#include <sys/stat.h>
  34 +#include <fcntl.h>
  35 +
  36 +#include <vector>
  37 +#include <algorithm>
  38 +
  39 +#include <srs_core_error.hpp>
  40 +#include <srs_core_log.hpp>
  41 +#include <srs_core_autofree.hpp>
  42 +
  43 +#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR)
  44 +
  45 +int64_t FILE_SIZE(int fd)
  46 +{
  47 + int64_t pre = FILE_OFFSET(fd);
  48 + int64_t pos = lseek(fd, 0, SEEK_END);
  49 + lseek(fd, pre, SEEK_SET);
  50 + return pos;
  51 +}
  52 +
  53 +#define LF (char)0x0a
  54 +#define CR (char)0x0d
  55 +
  56 +bool is_common_space(char ch)
  57 +{
  58 + return (ch == ' ' || ch == '\t' || ch == CR || ch == LF);
  59 +}
  60 +
  61 +#define CONF_BUFFER_SIZE 1024 * 1024
  62 +
  63 +SrsFileBuffer::SrsFileBuffer()
  64 +{
  65 + fd = -1;
  66 + line = 0;
  67 +
  68 + pos = last = start = new char[CONF_BUFFER_SIZE];
  69 + end = start + CONF_BUFFER_SIZE;
  70 +}
  71 +
  72 +SrsFileBuffer::~SrsFileBuffer()
  73 +{
  74 + if (fd > 0) {
  75 + close(fd);
  76 + }
  77 + srs_freepa(start);
  78 +}
  79 +
  80 +int SrsFileBuffer::open(const char* filename)
  81 +{
  82 + assert(fd == -1);
  83 +
  84 + if ((fd = ::open(filename, O_RDONLY, 0)) < 0) {
  85 + srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno));
  86 + return ERROR_SYSTEM_CONFIG_INVALID;
  87 + }
  88 +
  89 + line = 1;
  90 +
  91 + return ERROR_SUCCESS;
  92 +}
  93 +
  94 +SrsConfDirective::SrsConfDirective()
  95 +{
  96 +}
  97 +
  98 +SrsConfDirective::~SrsConfDirective()
  99 +{
  100 + std::vector<SrsConfDirective*>::iterator it;
  101 + for (it = directives.begin(); it != directives.end(); ++it) {
  102 + SrsConfDirective* directive = *it;
  103 + srs_freep(directive);
  104 + }
  105 + directives.clear();
  106 +}
  107 +
  108 +std::string SrsConfDirective::arg0()
  109 +{
  110 + if (args.size() > 0) {
  111 + return args.at(0);
  112 + }
  113 +
  114 + return "";
  115 +}
  116 +
  117 +std::string SrsConfDirective::arg1()
  118 +{
  119 + if (args.size() > 1) {
  120 + return args.at(1);
  121 + }
  122 +
  123 + return "";
  124 +}
  125 +
  126 +std::string SrsConfDirective::arg2()
  127 +{
  128 + if (args.size() > 2) {
  129 + return args.at(2);
  130 + }
  131 +
  132 + return "";
  133 +}
  134 +
  135 +SrsConfDirective* SrsConfDirective::at(int index)
  136 +{
  137 + return directives.at(index);
  138 +}
  139 +
  140 +SrsConfDirective* SrsConfDirective::get(std::string _name)
  141 +{
  142 + std::vector<SrsConfDirective*>::iterator it;
  143 + for (it = directives.begin(); it != directives.end(); ++it) {
  144 + SrsConfDirective* directive = *it;
  145 + if (directive->name == _name) {
  146 + return directive;
  147 + }
  148 + }
  149 +
  150 + return NULL;
  151 +}
  152 +
  153 +int SrsConfDirective::parse(const char* filename)
  154 +{
  155 + int ret = ERROR_SUCCESS;
  156 +
  157 + SrsFileBuffer buffer;
  158 +
  159 + if ((ret = buffer.open(filename)) != ERROR_SUCCESS) {
  160 + return ret;
  161 + }
  162 +
  163 + return parse_conf(&buffer, parse_file);
  164 +}
  165 +
  166 +// see: ngx_conf_parse
  167 +int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type)
  168 +{
  169 + int ret = ERROR_SUCCESS;
  170 +
  171 + while (true) {
  172 + std::vector<std::string> args;
  173 + ret = read_token(buffer, args);
  174 +
  175 + /**
  176 + * ret maybe:
  177 + * ERROR_SYSTEM_CONFIG_INVALID error.
  178 + * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found
  179 + * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found
  180 + * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found
  181 + * ERROR_SYSTEM_CONFIG_EOF the config file is done
  182 + */
  183 + if (ret == ERROR_SYSTEM_CONFIG_INVALID) {
  184 + return ret;
  185 + }
  186 + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) {
  187 + if (type != parse_block) {
  188 + srs_error("line %d: unexpected \"}\"", buffer->line);
  189 + return ret;
  190 + }
  191 + return ERROR_SUCCESS;
  192 + }
  193 + if (ret == ERROR_SYSTEM_CONFIG_EOF) {
  194 + if (type == parse_block) {
  195 + srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line);
  196 + return ret;
  197 + }
  198 + return ERROR_SUCCESS;
  199 + }
  200 +
  201 + if (args.empty()) {
  202 + srs_error("line %d: empty directive.", buffer->line);
  203 + return ret;
  204 + }
  205 +
  206 + // build directive tree.
  207 + SrsConfDirective* directive = new SrsConfDirective();
  208 +
  209 + directive->conf_line = buffer->line;
  210 + directive->name = args[0];
  211 + args.erase(args.begin());
  212 + directive->args.swap(args);
  213 +
  214 + directives.push_back(directive);
  215 +
  216 + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) {
  217 + if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) {
  218 + return ret;
  219 + }
  220 + }
  221 + }
  222 +
  223 + return ret;
  224 +}
  225 +
  226 +// see: ngx_conf_read_token
  227 +int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector<std::string>& args)
  228 +{
  229 + int ret = ERROR_SUCCESS;
  230 +
  231 + char* pstart = buffer->pos;
  232 + int startline = buffer->line;
  233 +
  234 + bool sharp_comment = false;
  235 +
  236 + bool d_quoted = false;
  237 + bool s_quoted = false;
  238 +
  239 + bool need_space = false;
  240 + bool last_space = true;
  241 +
  242 + while (true) {
  243 + if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) {
  244 + if (!args.empty() || !last_space) {
  245 + srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line);
  246 + return ERROR_SYSTEM_CONFIG_INVALID;
  247 + }
  248 + return ret;
  249 + }
  250 +
  251 + char ch = *buffer->pos++;
  252 +
  253 + if (ch == LF) {
  254 + buffer->line++;
  255 + sharp_comment = false;
  256 + }
  257 +
  258 + if (sharp_comment) {
  259 + continue;
  260 + }
  261 +
  262 + if (need_space) {
  263 + if (is_common_space(ch)) {
  264 + last_space = true;
  265 + need_space = false;
  266 + continue;
  267 + }
  268 + if (ch == ';') {
  269 + return ERROR_SYSTEM_CONFIG_DIRECTIVE;
  270 + }
  271 + if (ch == '{') {
  272 + return ERROR_SYSTEM_CONFIG_BLOCK_START;
  273 + }
  274 + srs_error("line %d: unexpected '%c'", buffer->line, ch);
  275 + return ERROR_SYSTEM_CONFIG_INVALID;
  276 + }
  277 +
  278 + // last charecter is space.
  279 + if (last_space) {
  280 + if (is_common_space(ch)) {
  281 + continue;
  282 + }
  283 + pstart = buffer->pos - 1;
  284 + startline = buffer->line;
  285 + switch (ch) {
  286 + case ';':
  287 + if (args.size() == 0) {
  288 + srs_error("line %d: unexpected ';'", buffer->line);
  289 + return ERROR_SYSTEM_CONFIG_INVALID;
  290 + }
  291 + return ERROR_SYSTEM_CONFIG_DIRECTIVE;
  292 + case '{':
  293 + if (args.size() == 0) {
  294 + srs_error("line %d: unexpected '{'", buffer->line);
  295 + return ERROR_SYSTEM_CONFIG_INVALID;
  296 + }
  297 + return ERROR_SYSTEM_CONFIG_BLOCK_START;
  298 + case '}':
  299 + if (args.size() != 0) {
  300 + srs_error("line %d: unexpected '}'", buffer->line);
  301 + return ERROR_SYSTEM_CONFIG_INVALID;
  302 + }
  303 + return ERROR_SYSTEM_CONFIG_BLOCK_END;
  304 + case '#':
  305 + sharp_comment = 1;
  306 + continue;
  307 + case '"':
  308 + pstart++;
  309 + d_quoted = true;
  310 + last_space = 0;
  311 + continue;
  312 + case '\'':
  313 + pstart++;
  314 + s_quoted = true;
  315 + last_space = 0;
  316 + continue;
  317 + default:
  318 + last_space = 0;
  319 + continue;
  320 + }
  321 + } else {
  322 + // last charecter is not space
  323 + bool found = false;
  324 + if (d_quoted) {
  325 + if (ch == '"') {
  326 + d_quoted = false;
  327 + need_space = true;
  328 + found = true;
  329 + }
  330 + } else if (s_quoted) {
  331 + if (ch == '\'') {
  332 + s_quoted = false;
  333 + need_space = true;
  334 + found = true;
  335 + }
  336 + } else if (is_common_space(ch) || ch == ';' || ch == '{') {
  337 + last_space = true;
  338 + found = 1;
  339 + }
  340 +
  341 + if (found) {
  342 + int len = buffer->pos - pstart;
  343 + char* word = new char[len];
  344 + memcpy(word, pstart, len);
  345 + word[len - 1] = 0;
  346 +
  347 + std::string word_str = word;
  348 + if (!word_str.empty()) {
  349 + args.push_back(word_str);
  350 + }
  351 + srs_freepa(word);
  352 +
  353 + if (ch == ';') {
  354 + return ERROR_SYSTEM_CONFIG_DIRECTIVE;
  355 + }
  356 + if (ch == '{') {
  357 + return ERROR_SYSTEM_CONFIG_BLOCK_START;
  358 + }
  359 + }
  360 + }
  361 + }
  362 +
  363 + return ret;
  364 +}
  365 +
  366 +int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart)
  367 +{
  368 + int ret = ERROR_SUCCESS;
  369 +
  370 + if (buffer->pos < buffer->last) {
  371 + return ret;
  372 + }
  373 +
  374 + int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd);
  375 + if (size > CONF_BUFFER_SIZE) {
  376 + ret = ERROR_SYSTEM_CONFIG_TOO_LARGE;
  377 + srs_error("config file too large, max=%d, actual=%d, ret=%d",
  378 + CONF_BUFFER_SIZE, size, ret);
  379 + return ret;
  380 + }
  381 +
  382 + if (size <= 0) {
  383 + return ERROR_SYSTEM_CONFIG_EOF;
  384 + }
  385 +
  386 + int len = buffer->pos - buffer->start;
  387 + if (len >= CONF_BUFFER_SIZE) {
  388 + buffer->line = startline;
  389 +
  390 + if (!d_quoted && !s_quoted) {
  391 + srs_error("line %d: too long parameter \"%*s...\" started",
  392 + buffer->line, 10, buffer->start);
  393 +
  394 + } else {
  395 + srs_error("line %d: too long parameter, "
  396 + "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\'');
  397 + }
  398 + return ERROR_SYSTEM_CONFIG_INVALID;
  399 + }
  400 +
  401 + if (len) {
  402 + memmove(buffer->start, pstart, len);
  403 + }
  404 +
  405 + size = srs_min(size, buffer->end - (buffer->start + len));
  406 + int n = read(buffer->fd, buffer->start + len, size);
  407 + if (n != size) {
  408 + srs_error("read file read error. expect %d, actual %d bytes.", size, n);
  409 + return ERROR_SYSTEM_CONFIG_INVALID;
  410 + }
  411 +
  412 + buffer->pos = buffer->start + len;
  413 + buffer->last = buffer->pos + n;
  414 + pstart = buffer->start;
  415 +
  416 + return ret;
  417 +}
  418 +
  419 +SrsConfig* config = new SrsConfig();
  420 +
  421 +SrsConfig::SrsConfig()
  422 +{
  423 + show_help = false;
  424 + show_version = false;
  425 +
  426 + root = new SrsConfDirective();
  427 + root->conf_line = 0;
  428 + root->name = "root";
  429 +}
  430 +
  431 +SrsConfig::~SrsConfig()
  432 +{
  433 + srs_freep(root);
  434 +}
  435 +
  436 +int SrsConfig::reload()
  437 +{
  438 + int ret = ERROR_SUCCESS;
  439 +
  440 + SrsConfig conf;
  441 + if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) {
  442 + srs_error("config reloader parse file failed. ret=%d", ret);
  443 + return ret;
  444 + }
  445 + srs_info("config reloader parse file success.");
  446 +
  447 + // store current root to old_root,
  448 + // and reap the root from conf to current root.
  449 + SrsConfDirective* old_root = root;
  450 + SrsAutoFree(SrsConfDirective, old_root, false);
  451 +
  452 + root = conf.root;
  453 + conf.root = NULL;
  454 +
  455 + // merge config.
  456 + std::vector<SrsReloadHandler*>::iterator it;
  457 +
  458 + // merge config: listen
  459 + if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) {
  460 + for (it = subscribes.begin(); it != subscribes.end(); ++it) {
  461 + SrsReloadHandler* subscribe = *it;
  462 + if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) {
  463 + srs_error("notify subscribes reload listen failed. ret=%d", ret);
  464 + return ret;
  465 + }
  466 + }
  467 + srs_trace("reload listen success.");
  468 + }
  469 + // merge config: pithy_print
  470 + if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) {
  471 + for (it = subscribes.begin(); it != subscribes.end(); ++it) {
  472 + SrsReloadHandler* subscribe = *it;
  473 + if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) {
  474 + srs_error("notify subscribes pithy_print listen failed. ret=%d", ret);
  475 + return ret;
  476 + }
  477 + }
  478 + srs_trace("reload pithy_print success.");
  479 + }
  480 +
  481 + // TODO: suppor reload hls/forward/ffmpeg/http
  482 +
  483 + return ret;
  484 +}
  485 +
  486 +void SrsConfig::subscribe(SrsReloadHandler* handler)
  487 +{
  488 + std::vector<SrsReloadHandler*>::iterator it;
  489 +
  490 + it = std::find(subscribes.begin(), subscribes.end(), handler);
  491 + if (it != subscribes.end()) {
  492 + return;
  493 + }
  494 +
  495 + subscribes.push_back(handler);
  496 +}
  497 +
  498 +void SrsConfig::unsubscribe(SrsReloadHandler* handler)
  499 +{
  500 + std::vector<SrsReloadHandler*>::iterator it;
  501 +
  502 + it = std::find(subscribes.begin(), subscribes.end(), handler);
  503 + if (it == subscribes.end()) {
  504 + return;
  505 + }
  506 +
  507 + subscribes.erase(it);
  508 +}
  509 +
  510 +// see: ngx_get_options
  511 +int SrsConfig::parse_options(int argc, char** argv)
  512 +{
  513 + int ret = ERROR_SUCCESS;
  514 +
  515 + for (int i = 1; i < argc; i++) {
  516 + if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) {
  517 + return ret;
  518 + }
  519 + }
  520 +
  521 + if (show_help) {
  522 + print_help(argv);
  523 + }
  524 +
  525 + if (show_version) {
  526 + printf("%s\n", RTMP_SIG_SRS_VERSION);
  527 + }
  528 +
  529 + if (show_help || show_version) {
  530 + exit(0);
  531 + }
  532 +
  533 + if (config_file.empty()) {
  534 + ret = ERROR_SYSTEM_CONFIG_INVALID;
  535 + srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret);
  536 + return ret;
  537 + }
  538 +
  539 + return parse_file(config_file.c_str());
  540 +}
  541 +
  542 +SrsConfDirective* SrsConfig::get_vhost(std::string vhost)
  543 +{
  544 + srs_assert(root);
  545 +
  546 + for (int i = 0; i < (int)root->directives.size(); i++) {
  547 + SrsConfDirective* conf = root->at(i);
  548 +
  549 + if (conf->name != "vhost") {
  550 + continue;
  551 + }
  552 +
  553 + if (conf->arg0() == vhost) {
  554 + return conf;
  555 + }
  556 + }
  557 +
  558 + if (vhost != RTMP_VHOST_DEFAULT) {
  559 + return get_vhost(RTMP_VHOST_DEFAULT);
  560 + }
  561 +
  562 + return NULL;
  563 +}
  564 +
  565 +SrsConfDirective* SrsConfig::get_vhost_on_connect(std::string vhost)
  566 +{
  567 + SrsConfDirective* conf = get_vhost(vhost);
  568 +
  569 + if (!conf) {
  570 + return NULL;
  571 + }
  572 +
  573 + conf = conf->get("http_hooks");
  574 + if (!conf) {
  575 + return NULL;
  576 + }
  577 +
  578 + return conf->get("on_connect");
  579 +}
  580 +
  581 +bool SrsConfig::get_vhost_enabled(std::string vhost)
  582 +{
  583 + SrsConfDirective* vhost_conf = get_vhost(vhost);
  584 +
  585 + if (!vhost_conf) {
  586 + return true;
  587 + }
  588 +
  589 + SrsConfDirective* conf = vhost_conf->get("enabled");
  590 + if (!conf) {
  591 + return true;
  592 + }
  593 +
  594 + if (conf->arg0() == "off") {
  595 + return false;
  596 + }
  597 +
  598 + return true;
  599 +}
  600 +
  601 +SrsConfDirective* SrsConfig::get_transcode(std::string vhost, std::string scope)
  602 +{
  603 + SrsConfDirective* conf = get_vhost(vhost);
  604 +
  605 + if (!conf) {
  606 + return NULL;
  607 + }
  608 +
  609 + SrsConfDirective* transcode = conf->get("transcode");
  610 + if (!transcode) {
  611 + return NULL;
  612 + }
  613 +
  614 + if (transcode->arg0() == scope) {
  615 + return transcode;
  616 + }
  617 +
  618 + return NULL;
  619 +}
  620 +
  621 +bool SrsConfig::get_transcode_enabled(SrsConfDirective* transcode)
  622 +{
  623 + if (!transcode) {
  624 + return false;
  625 + }
  626 +
  627 + SrsConfDirective* conf = transcode->get("enabled");
  628 + if (!conf || conf->arg0() != "on") {
  629 + return false;
  630 + }
  631 +
  632 + return true;
  633 +}
  634 +
  635 +std::string SrsConfig::get_transcode_ffmpeg(SrsConfDirective* transcode)
  636 +{
  637 + if (!transcode) {
  638 + return "";
  639 + }
  640 +
  641 + SrsConfDirective* conf = transcode->get("ffmpeg");
  642 + if (!conf) {
  643 + return "";
  644 + }
  645 +
  646 + return conf->arg0();
  647 +}
  648 +
  649 +void SrsConfig::get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines)
  650 +{
  651 + if (!transcode) {
  652 + return;
  653 + }
  654 +
  655 + for (int i = 0; i < (int)transcode->directives.size(); i++) {
  656 + SrsConfDirective* conf = transcode->directives[i];
  657 +
  658 + if (conf->name == "engine") {
  659 + engines.push_back(conf);
  660 + }
  661 + }
  662 +
  663 + return;
  664 +}
  665 +
  666 +bool SrsConfig::get_engine_enabled(SrsConfDirective* engine)
  667 +{
  668 + if (!engine) {
  669 + return false;
  670 + }
  671 +
  672 + SrsConfDirective* conf = engine->get("enabled");
  673 + if (!conf || conf->arg0() != "on") {
  674 + return false;
  675 + }
  676 +
  677 + return true;
  678 +}
  679 +
  680 +std::string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
  681 +{
  682 + if (!engine) {
  683 + return "";
  684 + }
  685 +
  686 + SrsConfDirective* conf = engine->get("vcodec");
  687 + if (!conf) {
  688 + return "";
  689 + }
  690 +
  691 + return conf->arg0();
  692 +}
  693 +
  694 +int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
  695 +{
  696 + if (!engine) {
  697 + return 0;
  698 + }
  699 +
  700 + SrsConfDirective* conf = engine->get("vbitrate");
  701 + if (!conf) {
  702 + return 0;
  703 + }
  704 +
  705 + return ::atoi(conf->arg0().c_str());
  706 +}
  707 +
  708 +double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
  709 +{
  710 + if (!engine) {
  711 + return 0;
  712 + }
  713 +
  714 + SrsConfDirective* conf = engine->get("vfps");
  715 + if (!conf) {
  716 + return 0;
  717 + }
  718 +
  719 + return ::atof(conf->arg0().c_str());
  720 +}
  721 +
  722 +int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
  723 +{
  724 + if (!engine) {
  725 + return 0;
  726 + }
  727 +
  728 + SrsConfDirective* conf = engine->get("vwidth");
  729 + if (!conf) {
  730 + return 0;
  731 + }
  732 +
  733 + return ::atoi(conf->arg0().c_str());
  734 +}
  735 +
  736 +int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
  737 +{
  738 + if (!engine) {
  739 + return 0;
  740 + }
  741 +
  742 + SrsConfDirective* conf = engine->get("vheight");
  743 + if (!conf) {
  744 + return 0;
  745 + }
  746 +
  747 + return ::atoi(conf->arg0().c_str());
  748 +}
  749 +
  750 +int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
  751 +{
  752 + if (!engine) {
  753 + return 0;
  754 + }
  755 +
  756 + SrsConfDirective* conf = engine->get("vthreads");
  757 + if (!conf) {
  758 + return 0;
  759 + }
  760 +
  761 + return ::atoi(conf->arg0().c_str());
  762 +}
  763 +
  764 +std::string SrsConfig::get_engine_vprofile(SrsConfDirective* engine)
  765 +{
  766 + if (!engine) {
  767 + return "";
  768 + }
  769 +
  770 + SrsConfDirective* conf = engine->get("vprofile");
  771 + if (!conf) {
  772 + return "";
  773 + }
  774 +
  775 + return conf->arg0();
  776 +}
  777 +
  778 +std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
  779 +{
  780 + if (!engine) {
  781 + return "";
  782 + }
  783 +
  784 + SrsConfDirective* conf = engine->get("vpreset");
  785 + if (!conf) {
  786 + return "";
  787 + }
  788 +
  789 + return conf->arg0();
  790 +}
  791 +
  792 +void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams)
  793 +{
  794 + if (!engine) {
  795 + return;
  796 + }
  797 +
  798 + SrsConfDirective* conf = engine->get("vparams");
  799 + if (!conf) {
  800 + return;
  801 + }
  802 +
  803 + for (int i = 0; i < (int)conf->directives.size(); i++) {
  804 + SrsConfDirective* p = conf->directives[i];
  805 + if (!p) {
  806 + continue;
  807 + }
  808 +
  809 + vparams.push_back("-" + p->name);
  810 + vparams.push_back(p->arg0());
  811 + }
  812 +}
  813 +
  814 +void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter)
  815 +{
  816 + if (!engine) {
  817 + return;
  818 + }
  819 +
  820 + SrsConfDirective* conf = engine->get("vfilter");
  821 + if (!conf) {
  822 + return;
  823 + }
  824 +
  825 + for (int i = 0; i < (int)conf->directives.size(); i++) {
  826 + SrsConfDirective* p = conf->directives[i];
  827 + if (!p) {
  828 + continue;
  829 + }
  830 +
  831 + vfilter.push_back("-" + p->name);
  832 + vfilter.push_back(p->arg0());
  833 + }
  834 +}
  835 +
  836 +std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
  837 +{
  838 + if (!engine) {
  839 + return "";
  840 + }
  841 +
  842 + SrsConfDirective* conf = engine->get("acodec");
  843 + if (!conf) {
  844 + return "";
  845 + }
  846 +
  847 + return conf->arg0();
  848 +}
  849 +
  850 +int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
  851 +{
  852 + if (!engine) {
  853 + return 0;
  854 + }
  855 +
  856 + SrsConfDirective* conf = engine->get("abitrate");
  857 + if (!conf) {
  858 + return 0;
  859 + }
  860 +
  861 + return ::atoi(conf->arg0().c_str());
  862 +}
  863 +
  864 +int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
  865 +{
  866 + if (!engine) {
  867 + return 0;
  868 + }
  869 +
  870 + SrsConfDirective* conf = engine->get("asample_rate");
  871 + if (!conf) {
  872 + return 0;
  873 + }
  874 +
  875 + return ::atoi(conf->arg0().c_str());
  876 +}
  877 +
  878 +int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
  879 +{
  880 + if (!engine) {
  881 + return 0;
  882 + }
  883 +
  884 + SrsConfDirective* conf = engine->get("achannels");
  885 + if (!conf) {
  886 + return 0;
  887 + }
  888 +
  889 + return ::atoi(conf->arg0().c_str());
  890 +}
  891 +
  892 +void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams)
  893 +{
  894 + if (!engine) {
  895 + return;
  896 + }
  897 +
  898 + SrsConfDirective* conf = engine->get("aparams");
  899 + if (!conf) {
  900 + return;
  901 + }
  902 +
  903 + for (int i = 0; i < (int)conf->directives.size(); i++) {
  904 + SrsConfDirective* p = conf->directives[i];
  905 + if (!p) {
  906 + continue;
  907 + }
  908 +
  909 + aparams.push_back("-" + p->name);
  910 + aparams.push_back(p->arg0());
  911 + }
  912 +}
  913 +
  914 +std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
  915 +{
  916 + if (!engine) {
  917 + return "";
  918 + }
  919 +
  920 + SrsConfDirective* conf = engine->get("output");
  921 + if (!conf) {
  922 + return "";
  923 + }
  924 +
  925 + return conf->arg0();
  926 +}
  927 +
  928 +std::string SrsConfig::get_log_dir()
  929 +{
  930 + srs_assert(root);
  931 +
  932 + SrsConfDirective* conf = root->get("log_dir");
  933 + if (!conf || conf->arg0().empty()) {
  934 + return "./objs/logs";
  935 + }
  936 +
  937 + return conf->arg0();
  938 +}
  939 +
  940 +int SrsConfig::get_max_connections()
  941 +{
  942 + srs_assert(root);
  943 +
  944 + SrsConfDirective* conf = root->get("max_connections");
  945 + if (!conf || conf->arg0().empty()) {
  946 + return 2000;
  947 + }
  948 +
  949 + return ::atoi(conf->arg0().c_str());
  950 +}
  951 +
  952 +SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost)
  953 +{
  954 + SrsConfDirective* conf = get_vhost(vhost);
  955 +
  956 + if (!conf) {
  957 + return NULL;
  958 + }
  959 +
  960 + return conf->get("gop_cache");
  961 +}
  962 +
  963 +SrsConfDirective* SrsConfig::get_forward(std::string vhost)
  964 +{
  965 + SrsConfDirective* conf = get_vhost(vhost);
  966 +
  967 + if (!conf) {
  968 + return NULL;
  969 + }
  970 +
  971 + return conf->get("forward");
  972 +}
  973 +
  974 +SrsConfDirective* SrsConfig::get_hls(std::string vhost)
  975 +{
  976 + SrsConfDirective* conf = get_vhost(vhost);
  977 +
  978 + if (!conf) {
  979 + return NULL;
  980 + }
  981 +
  982 + return conf->get("hls");
  983 +}
  984 +
  985 +bool SrsConfig::get_hls_enabled(std::string vhost)
  986 +{
  987 + SrsConfDirective* hls = get_hls(vhost);
  988 +
  989 + if (!hls) {
  990 + return true;
  991 + }
  992 +
  993 + if (hls->arg0() == "off") {
  994 + return false;
  995 + }
  996 +
  997 + return true;
  998 +}
  999 +
  1000 +SrsConfDirective* SrsConfig::get_hls_path(std::string vhost)
  1001 +{
  1002 + SrsConfDirective* conf = get_vhost(vhost);
  1003 +
  1004 + if (!conf) {
  1005 + return NULL;
  1006 + }
  1007 +
  1008 + return conf->get("hls_path");
  1009 +}
  1010 +
  1011 +SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost)
  1012 +{
  1013 + SrsConfDirective* conf = get_vhost(vhost);
  1014 +
  1015 + if (!conf) {
  1016 + return NULL;
  1017 + }
  1018 +
  1019 + return conf->get("hls_fragment");
  1020 +}
  1021 +
  1022 +SrsConfDirective* SrsConfig::get_hls_window(std::string vhost)
  1023 +{
  1024 + SrsConfDirective* conf = get_vhost(vhost);
  1025 +
  1026 + if (!conf) {
  1027 + return NULL;
  1028 + }
  1029 +
  1030 + return conf->get("hls_window");
  1031 +}
  1032 +
  1033 +SrsConfDirective* SrsConfig::get_refer(std::string vhost)
  1034 +{
  1035 + SrsConfDirective* conf = get_vhost(vhost);
  1036 +
  1037 + if (!conf) {
  1038 + return NULL;
  1039 + }
  1040 +
  1041 + return conf->get("refer");
  1042 +}
  1043 +
  1044 +SrsConfDirective* SrsConfig::get_refer_play(std::string vhost)
  1045 +{
  1046 + SrsConfDirective* conf = get_vhost(vhost);
  1047 +
  1048 + if (!conf) {
  1049 + return NULL;
  1050 + }
  1051 +
  1052 + return conf->get("refer_play");
  1053 +}
  1054 +
  1055 +SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost)
  1056 +{
  1057 + SrsConfDirective* conf = get_vhost(vhost);
  1058 +
  1059 + if (!conf) {
  1060 + return NULL;
  1061 + }
  1062 +
  1063 + return conf->get("refer_publish");
  1064 +}
  1065 +
  1066 +SrsConfDirective* SrsConfig::get_listen()
  1067 +{
  1068 + return root->get("listen");
  1069 +}
  1070 +
  1071 +SrsConfDirective* SrsConfig::get_chunk_size()
  1072 +{
  1073 + return root->get("chunk_size");
  1074 +}
  1075 +
  1076 +SrsConfDirective* SrsConfig::get_pithy_print_publish()
  1077 +{
  1078 + SrsConfDirective* pithy = root->get("pithy_print");
  1079 + if (!pithy) {
  1080 + return NULL;
  1081 + }
  1082 +
  1083 + return pithy->get("publish");
  1084 +}
  1085 +
  1086 +SrsConfDirective* SrsConfig::get_pithy_print_forwarder()
  1087 +{
  1088 + SrsConfDirective* pithy = root->get("pithy_print");
  1089 + if (!pithy) {
  1090 + return NULL;
  1091 + }
  1092 +
  1093 + return pithy->get("forwarder");
  1094 +}
  1095 +
  1096 +SrsConfDirective* SrsConfig::get_pithy_print_hls()
  1097 +{
  1098 + SrsConfDirective* pithy = root->get("pithy_print");
  1099 + if (!pithy) {
  1100 + return NULL;
  1101 + }
  1102 +
  1103 + return pithy->get("hls");
  1104 +}
  1105 +
  1106 +SrsConfDirective* SrsConfig::get_pithy_print_encoder()
  1107 +{
  1108 + SrsConfDirective* pithy = root->get("encoder");
  1109 + if (!pithy) {
  1110 + return NULL;
  1111 + }
  1112 +
  1113 + return pithy->get("forwarder");
  1114 +}
  1115 +
  1116 +SrsConfDirective* SrsConfig::get_pithy_print_play()
  1117 +{
  1118 + SrsConfDirective* pithy = root->get("pithy_print");
  1119 + if (!pithy) {
  1120 + return NULL;
  1121 + }
  1122 +
  1123 + return pithy->get("play");
  1124 +}
  1125 +
  1126 +int SrsConfig::parse_file(const char* filename)
  1127 +{
  1128 + int ret = ERROR_SUCCESS;
  1129 +
  1130 + config_file = filename;
  1131 +
  1132 + if (config_file.empty()) {
  1133 + return ERROR_SYSTEM_CONFIG_INVALID;
  1134 + }
  1135 +
  1136 + if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) {
  1137 + return ret;
  1138 + }
  1139 +
  1140 + SrsConfDirective* conf = NULL;
  1141 + if ((conf = get_listen()) == NULL || conf->args.size() == 0) {
  1142 + ret = ERROR_SYSTEM_CONFIG_INVALID;
  1143 + srs_error("line %d: conf error, "
  1144 + "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret);
  1145 + return ret;
  1146 + }
  1147 + // TODO: check the hls.
  1148 + // TODO: check other config.
  1149 + // TODO: check hls.
  1150 + // TODO: check ssl.
  1151 + // TODO: check ffmpeg.
  1152 + // TODO: check http.
  1153 +
  1154 + return ret;
  1155 +}
  1156 +
  1157 +int SrsConfig::parse_argv(int& i, char** argv)
  1158 +{
  1159 + int ret = ERROR_SUCCESS;
  1160 +
  1161 + char* p = argv[i];
  1162 +
  1163 + if (*p++ != '-') {
  1164 + ret = ERROR_SYSTEM_CONFIG_INVALID;
  1165 + srs_error("invalid options(index=%d, value=%s), "
  1166 + "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret);
  1167 + return ret;
  1168 + }
  1169 +
  1170 + while (*p) {
  1171 + switch (*p++) {
  1172 + case '?':
  1173 + case 'h':
  1174 + show_help = true;
  1175 + break;
  1176 + case 'v':
  1177 + case 'V':
  1178 + show_version = true;
  1179 + break;
  1180 + case 'c':
  1181 + if (*p) {
  1182 + config_file = p;
  1183 + return ret;
  1184 + }
  1185 + if (argv[++i]) {
  1186 + config_file = argv[i];
  1187 + return ret;
  1188 + }
  1189 + ret = ERROR_SYSTEM_CONFIG_INVALID;
  1190 + srs_error("option \"-c\" requires parameter, ret=%d", ret);
  1191 + return ret;
  1192 + default:
  1193 + ret = ERROR_SYSTEM_CONFIG_INVALID;
  1194 + srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret);
  1195 + return ret;
  1196 + }
  1197 + }
  1198 +
  1199 + return ret;
  1200 +}
  1201 +
  1202 +void SrsConfig::print_help(char** argv)
  1203 +{
  1204 + printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION
  1205 + " Copyright (c) 2013 winlin\n"
  1206 + "Contributors: "RTMP_SIG_SRS_CONTRIBUTOR"\n"
  1207 + "Build: "SRS_BUILD_DATE" Configuration: "SRS_CONFIGURE"\n"
  1208 + "Usage: %s [-h?vV] [-c <filename>]\n"
  1209 + "\n"
  1210 + "Options:\n"
  1211 + " -?-h : show help\n"
  1212 + " -v-V : show version and exit\n"
  1213 + " -c filename : set configuration file\n"
  1214 + "\n"
  1215 + RTMP_SIG_SRS_WEB"\n"
  1216 + RTMP_SIG_SRS_URL"\n"
  1217 + "Email: "RTMP_SIG_SRS_EMAIL"\n"
  1218 + "\n",
  1219 + argv[0]);
  1220 +}
  1221 +
  1222 +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b)
  1223 +{
  1224 + if (!a || !b) {
  1225 + return false;
  1226 + }
  1227 +
  1228 + if (a->name != b->name) {
  1229 + return false;
  1230 + }
  1231 +
  1232 + if (a->args.size() != b->args.size()) {
  1233 + return false;
  1234 + }
  1235 +
  1236 + for (int i = 0; i < (int)a->args.size(); i++) {
  1237 + if (a->args.at(i) != b->args.at(i)) {
  1238 + return false;
  1239 + }
  1240 + }
  1241 +
  1242 + if (a->directives.size() != b->directives.size()) {
  1243 + return false;
  1244 + }
  1245 +
  1246 + for (int i = 0; i < (int)a->directives.size(); i++) {
  1247 + SrsConfDirective* a0 = a->at(i);
  1248 + SrsConfDirective* b0 = b->at(i);
  1249 +
  1250 + if (!srs_directive_equals(a0, b0)) {
  1251 + return false;
  1252 + }
  1253 + }
  1254 +
  1255 + return true;
  1256 +}
  1257 +
1 -/*  
2 -The MIT License (MIT)  
3 -  
4 -Copyright (c) 2013 winlin  
5 -  
6 -Permission is hereby granted, free of charge, to any person obtaining a copy of  
7 -this software and associated documentation files (the "Software"), to deal in  
8 -the Software without restriction, including without limitation the rights to  
9 -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of  
10 -the Software, and to permit persons to whom the Software is furnished to do so,  
11 -subject to the following conditions:  
12 -  
13 -The above copyright notice and this permission notice shall be included in all  
14 -copies or substantial portions of the Software.  
15 -  
16 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
17 -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS  
18 -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR  
19 -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER  
20 -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN  
21 -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
22 -*/  
23 -  
24 -#ifndef SRS_CORE_CONIFG_HPP  
25 -#define SRS_CORE_CONIFG_HPP  
26 -  
27 -/*  
28 -#include <srs_core_config.hpp>  
29 -*/  
30 -#include <srs_core.hpp>  
31 -  
32 -#include <vector>  
33 -#include <string>  
34 -  
35 -#include <srs_core_reload.hpp>  
36 -  
37 -// default vhost for rtmp  
38 -#define RTMP_VHOST_DEFAULT "__defaultVhost__"  
39 -  
40 -#define SRS_LOCALHOST "127.0.0.1"  
41 -#define RTMP_DEFAULT_PORT 1935  
42 -#define RTMP_DEFAULT_PORTS "1935"  
43 -  
44 -#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"  
45 -#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10  
46 -#define SRS_CONF_DEFAULT_HLS_WINDOW 60  
47 -// in ms, for HLS aac sync time.  
48 -#define SRS_CONF_DEFAULT_AAC_SYNC 100  
49 -// in ms, for HLS aac flush the audio  
50 -#define SRS_CONF_DEFAULT_AAC_DELAY 300  
51 -  
52 -class SrsFileBuffer  
53 -{  
54 -public:  
55 - int fd;  
56 - int line;  
57 - // start of buffer.  
58 - char* start;  
59 - // end of buffer.  
60 - char* end;  
61 - // current consumed position.  
62 - char* pos;  
63 - // last available position.  
64 - char* last;  
65 -  
66 - SrsFileBuffer();  
67 - virtual ~SrsFileBuffer();  
68 - virtual int open(const char* filename);  
69 -};  
70 -  
71 -class SrsConfDirective  
72 -{  
73 -public:  
74 - int conf_line;  
75 - std::string name;  
76 - std::vector<std::string> args;  
77 - std::vector<SrsConfDirective*> directives;  
78 -public:  
79 - SrsConfDirective();  
80 - virtual ~SrsConfDirective();  
81 - std::string arg0();  
82 - std::string arg1();  
83 - std::string arg2();  
84 - SrsConfDirective* at(int index);  
85 - SrsConfDirective* get(std::string _name);  
86 -public:  
87 - virtual int parse(const char* filename);  
88 -public:  
89 - enum SrsDirectiveType{parse_file, parse_block};  
90 - virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type);  
91 - virtual int read_token(SrsFileBuffer* buffer, std::vector<std::string>& args);  
92 - virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart);  
93 -};  
94 -  
95 -/**  
96 -* the config parser.  
97 -* for the config supports reload, so never keep the reference cross st-thread,  
98 -* that is, never save the SrsConfDirective* get by any api of config,  
99 -* for it maybe free in the reload st-thread cycle.  
100 -* you can keep it before st-thread switch, or simply never keep it.  
101 -*/  
102 -class SrsConfig  
103 -{  
104 -private:  
105 - bool show_help;  
106 - bool show_version;  
107 - std::string config_file;  
108 - SrsConfDirective* root;  
109 - std::vector<SrsReloadHandler*> subscribes;  
110 -public:  
111 - SrsConfig();  
112 - virtual ~SrsConfig();  
113 -public:  
114 - virtual int reload();  
115 - virtual void subscribe(SrsReloadHandler* handler);  
116 - virtual void unsubscribe(SrsReloadHandler* handler);  
117 -public:  
118 - virtual int parse_options(int argc, char** argv);  
119 -public:  
120 - virtual SrsConfDirective* get_vhost(std::string vhost);  
121 - virtual bool get_vhost_enabled(std::string vhost);  
122 - virtual SrsConfDirective* get_vhost_on_connect(std::string vhost);  
123 - virtual SrsConfDirective* get_transcode(std::string vhost, std::string scope);  
124 - virtual bool get_transcode_enabled(SrsConfDirective* transcode);  
125 - virtual std::string get_transcode_ffmpeg(SrsConfDirective* transcode);  
126 - virtual void get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines);  
127 - virtual bool get_engine_enabled(SrsConfDirective* engine);  
128 - virtual std::string get_engine_vcodec(SrsConfDirective* engine);  
129 - virtual int get_engine_vbitrate(SrsConfDirective* engine);  
130 - virtual double get_engine_vfps(SrsConfDirective* engine);  
131 - virtual int get_engine_vwidth(SrsConfDirective* engine);  
132 - virtual int get_engine_vheight(SrsConfDirective* engine);  
133 - virtual int get_engine_vthreads(SrsConfDirective* engine);  
134 - virtual std::string get_engine_vprofile(SrsConfDirective* engine);  
135 - virtual std::string get_engine_vpreset(SrsConfDirective* engine);  
136 - virtual void get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams);  
137 - virtual void get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter);  
138 - virtual std::string get_engine_acodec(SrsConfDirective* engine);  
139 - virtual int get_engine_abitrate(SrsConfDirective* engine);  
140 - virtual int get_engine_asample_rate(SrsConfDirective* engine);  
141 - virtual int get_engine_achannels(SrsConfDirective* engine);  
142 - virtual void get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams);  
143 - virtual std::string get_engine_output(SrsConfDirective* engine);  
144 - virtual std::string get_log_dir();  
145 - virtual int get_max_connections();  
146 - virtual SrsConfDirective* get_gop_cache(std::string vhost);  
147 - virtual SrsConfDirective* get_forward(std::string vhost);  
148 - virtual SrsConfDirective* get_hls(std::string vhost);  
149 - virtual bool get_hls_enabled(std::string vhost);  
150 - virtual SrsConfDirective* get_hls_path(std::string vhost);  
151 - virtual SrsConfDirective* get_hls_fragment(std::string vhost);  
152 - virtual SrsConfDirective* get_hls_window(std::string vhost);  
153 - virtual SrsConfDirective* get_refer(std::string vhost);  
154 - virtual SrsConfDirective* get_refer_play(std::string vhost);  
155 - virtual SrsConfDirective* get_refer_publish(std::string vhost);  
156 - virtual SrsConfDirective* get_listen();  
157 - virtual SrsConfDirective* get_chunk_size();  
158 - virtual SrsConfDirective* get_pithy_print_publish();  
159 - virtual SrsConfDirective* get_pithy_print_forwarder();  
160 - virtual SrsConfDirective* get_pithy_print_encoder();  
161 - virtual SrsConfDirective* get_pithy_print_hls();  
162 - virtual SrsConfDirective* get_pithy_print_play();  
163 -private:  
164 - virtual int parse_file(const char* filename);  
165 - virtual int parse_argv(int& i, char** argv);  
166 - virtual void print_help(char** argv);  
167 -};  
168 -  
169 -/**  
170 -* deep compare directive.  
171 -*/  
172 -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b);  
173 -  
174 -// global config  
175 -extern SrsConfig* config;  
176 - 1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013 winlin
  5 +
  6 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 +this software and associated documentation files (the "Software"), to deal in
  8 +the Software without restriction, including without limitation the rights to
  9 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 +the Software, and to permit persons to whom the Software is furnished to do so,
  11 +subject to the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be included in all
  14 +copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +
  24 +#ifndef SRS_CORE_CONIFG_HPP
  25 +#define SRS_CORE_CONIFG_HPP
  26 +
  27 +/*
  28 +#include <srs_core_config.hpp>
  29 +*/
  30 +#include <srs_core.hpp>
  31 +
  32 +#include <vector>
  33 +#include <string>
  34 +
  35 +#include <srs_core_reload.hpp>
  36 +
  37 +// default vhost for rtmp
  38 +#define RTMP_VHOST_DEFAULT "__defaultVhost__"
  39 +
  40 +#define SRS_LOCALHOST "127.0.0.1"
  41 +#define RTMP_DEFAULT_PORT 1935
  42 +#define RTMP_DEFAULT_PORTS "1935"
  43 +
  44 +#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
  45 +#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
  46 +#define SRS_CONF_DEFAULT_HLS_WINDOW 60
  47 +// in ms, for HLS aac sync time.
  48 +#define SRS_CONF_DEFAULT_AAC_SYNC 100
  49 +// in ms, for HLS aac flush the audio
  50 +#define SRS_CONF_DEFAULT_AAC_DELAY 300
  51 +
  52 +class SrsFileBuffer
  53 +{
  54 +public:
  55 + int fd;
  56 + int line;
  57 + // start of buffer.
  58 + char* start;
  59 + // end of buffer.
  60 + char* end;
  61 + // current consumed position.
  62 + char* pos;
  63 + // last available position.
  64 + char* last;
  65 +
  66 + SrsFileBuffer();
  67 + virtual ~SrsFileBuffer();
  68 + virtual int open(const char* filename);
  69 +};
  70 +
  71 +class SrsConfDirective
  72 +{
  73 +public:
  74 + int conf_line;
  75 + std::string name;
  76 + std::vector<std::string> args;
  77 + std::vector<SrsConfDirective*> directives;
  78 +public:
  79 + SrsConfDirective();
  80 + virtual ~SrsConfDirective();
  81 + std::string arg0();
  82 + std::string arg1();
  83 + std::string arg2();
  84 + SrsConfDirective* at(int index);
  85 + SrsConfDirective* get(std::string _name);
  86 +public:
  87 + virtual int parse(const char* filename);
  88 +public:
  89 + enum SrsDirectiveType{parse_file, parse_block};
  90 + virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type);
  91 + virtual int read_token(SrsFileBuffer* buffer, std::vector<std::string>& args);
  92 + virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart);
  93 +};
  94 +
  95 +/**
  96 +* the config parser.
  97 +* for the config supports reload, so never keep the reference cross st-thread,
  98 +* that is, never save the SrsConfDirective* get by any api of config,
  99 +* for it maybe free in the reload st-thread cycle.
  100 +* you can keep it before st-thread switch, or simply never keep it.
  101 +*/
  102 +class SrsConfig
  103 +{
  104 +private:
  105 + bool show_help;
  106 + bool show_version;
  107 + std::string config_file;
  108 + SrsConfDirective* root;
  109 + std::vector<SrsReloadHandler*> subscribes;
  110 +public:
  111 + SrsConfig();
  112 + virtual ~SrsConfig();
  113 +public:
  114 + virtual int reload();
  115 + virtual void subscribe(SrsReloadHandler* handler);
  116 + virtual void unsubscribe(SrsReloadHandler* handler);
  117 +public:
  118 + virtual int parse_options(int argc, char** argv);
  119 +public:
  120 + virtual SrsConfDirective* get_vhost(std::string vhost);
  121 + virtual bool get_vhost_enabled(std::string vhost);
  122 + virtual SrsConfDirective* get_vhost_on_connect(std::string vhost);
  123 + virtual SrsConfDirective* get_transcode(std::string vhost, std::string scope);
  124 + virtual bool get_transcode_enabled(SrsConfDirective* transcode);
  125 + virtual std::string get_transcode_ffmpeg(SrsConfDirective* transcode);
  126 + virtual void get_transcode_engines(SrsConfDirective* transcode, std::vector<SrsConfDirective*>& engines);
  127 + virtual bool get_engine_enabled(SrsConfDirective* engine);
  128 + virtual std::string get_engine_vcodec(SrsConfDirective* engine);
  129 + virtual int get_engine_vbitrate(SrsConfDirective* engine);
  130 + virtual double get_engine_vfps(SrsConfDirective* engine);
  131 + virtual int get_engine_vwidth(SrsConfDirective* engine);
  132 + virtual int get_engine_vheight(SrsConfDirective* engine);
  133 + virtual int get_engine_vthreads(SrsConfDirective* engine);
  134 + virtual std::string get_engine_vprofile(SrsConfDirective* engine);
  135 + virtual std::string get_engine_vpreset(SrsConfDirective* engine);
  136 + virtual void get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams);
  137 + virtual void get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter);
  138 + virtual std::string get_engine_acodec(SrsConfDirective* engine);
  139 + virtual int get_engine_abitrate(SrsConfDirective* engine);
  140 + virtual int get_engine_asample_rate(SrsConfDirective* engine);
  141 + virtual int get_engine_achannels(SrsConfDirective* engine);
  142 + virtual void get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams);
  143 + virtual std::string get_engine_output(SrsConfDirective* engine);
  144 + virtual std::string get_log_dir();
  145 + virtual int get_max_connections();
  146 + virtual SrsConfDirective* get_gop_cache(std::string vhost);
  147 + virtual SrsConfDirective* get_forward(std::string vhost);
  148 + virtual SrsConfDirective* get_hls(std::string vhost);
  149 + virtual bool get_hls_enabled(std::string vhost);
  150 + virtual SrsConfDirective* get_hls_path(std::string vhost);
  151 + virtual SrsConfDirective* get_hls_fragment(std::string vhost);
  152 + virtual SrsConfDirective* get_hls_window(std::string vhost);
  153 + virtual SrsConfDirective* get_refer(std::string vhost);
  154 + virtual SrsConfDirective* get_refer_play(std::string vhost);
  155 + virtual SrsConfDirective* get_refer_publish(std::string vhost);
  156 + virtual SrsConfDirective* get_listen();
  157 + virtual SrsConfDirective* get_chunk_size();
  158 + virtual SrsConfDirective* get_pithy_print_publish();
  159 + virtual SrsConfDirective* get_pithy_print_forwarder();
  160 + virtual SrsConfDirective* get_pithy_print_encoder();
  161 + virtual SrsConfDirective* get_pithy_print_hls();
  162 + virtual SrsConfDirective* get_pithy_print_play();
  163 +private:
  164 + virtual int parse_file(const char* filename);
  165 + virtual int parse_argv(int& i, char** argv);
  166 + virtual void print_help(char** argv);
  167 +};
  168 +
  169 +/**
  170 +* deep compare directive.
  171 +*/
  172 +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b);
  173 +
  174 +// global config
  175 +extern SrsConfig* config;
  176 +
177 #endif 177 #endif