int SrsSource::find(SrsRequest* req, SrsSource** ppsource) { int ret = ERROR_SUCCESS; string stream_url = req->get_stream_url(); string vhost = req->vhost; if (pool.find(stream_url) == pool.end()) { SrsSource* source = new SrsSource(req); if ((ret = source->initialize()) != ERROR_SUCCESS) { srs_freep(source); return ret; } pool[stream_url] = source; srs_info("create new source for url=%s, vhost=%s", stream_url.c_str(), vhost.c_str()); } // we always update the request of resource, // for origin auth is on, the token in request maybe invalid, // and we only need to update the token of request, it's simple. if (true) { SrsSource* source = pool[stream_url]; source->_req->update_auth(req); *ppsource = source; } return ret; }
int SrsEdgeIngester::process_publish_message(SrsMessage* msg) { int ret = ERROR_SUCCESS; SrsSource* source = _source; // process audio packet if (msg->header.is_audio()) { if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) { srs_error("source process audio message failed. ret=%d", ret); return ret; } } // process video packet if (msg->header.is_video()) { if ((ret = source->on_video(msg)) != ERROR_SUCCESS) { srs_error("source process video message failed. ret=%d", ret); return ret; } } // process aggregate packet if (msg->header.is_aggregate()) { if ((ret = source->on_aggregate(msg)) != ERROR_SUCCESS) { srs_error("source process aggregate message failed. ret=%d", ret); return ret; } return ret; } // process onMetaData if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { SrsPacket* pkt = NULL; if ((ret = client->decode_message(msg, &pkt)) != ERROR_SUCCESS) { srs_error("decode onMetaData message failed. ret=%d", ret); return ret; } SrsAutoFree(SrsPacket, pkt); if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) { SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt); if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) { srs_error("source process onMetaData message failed. ret=%d", ret); return ret; } srs_info("process onMetaData message success."); return ret; } srs_info("ignore AMF0/AMF3 data message."); return ret; } return ret; }
int SrsSource::find(SrsRequest* req, SrsSource** ppsource) { int ret = ERROR_SUCCESS; string stream_url = req->get_stream_url(); string vhost = req->vhost; if (pool.find(stream_url) == pool.end()) { SrsSource* source = new SrsSource(req); if ((ret = source->initialize()) != ERROR_SUCCESS) { srs_freep(source); return ret; } pool[stream_url] = source; srs_info("create new source for url=%s, vhost=%s", stream_url.c_str(), vhost.c_str()); } *ppsource = pool[stream_url]; return ret; }
int SrsRtmpConn::stream_service_cycle() { int ret = ERROR_SUCCESS; SrsRtmpConnType type; if ((ret = rtmp->identify_client(res->stream_id, type, req->stream, req->duration)) != ERROR_SUCCESS) { srs_error("identify client failed. ret=%d", ret); return ret; } req->strip(); srs_trace("identify client success. type=%s, stream_name=%s, duration=%.2f", srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration); // client is identified, set the timeout to service timeout. rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US); rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US); // set chunk size to larger. int chunk_size = _srs_config->get_chunk_size(req->vhost); if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) { srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret); return ret; } srs_trace("set chunk_size=%d success", chunk_size); // find a source to serve. SrsSource* source = NULL; if ((ret = SrsSource::find(req, &source)) != ERROR_SUCCESS) { return ret; } srs_assert(source != NULL); bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost); // check publish available // for edge, never check it, for edge use proxy mode. if (!vhost_is_edge) { if (type != SrsRtmpConnPlay && !source->can_publish()) { ret = ERROR_SYSTEM_STREAM_BUSY; srs_warn("stream %s is already publishing. ret=%d", req->get_stream_url().c_str(), ret); // to delay request st_usleep(SRS_STREAM_BUSY_SLEEP_US); return ret; } } bool enabled_cache = _srs_config->get_gop_cache(req->vhost); srs_trace("source found, url=%s, enabled_cache=%d, edge=%d", req->get_stream_url().c_str(), enabled_cache, vhost_is_edge); source->set_cache(enabled_cache); switch (type) { case SrsRtmpConnPlay: { srs_verbose("start to play stream %s.", req->stream.c_str()); if (vhost_is_edge) { if ((ret = source->on_edge_start_play()) != ERROR_SUCCESS) { srs_error("notice edge start play stream failed. ret=%d", ret); return ret; } } if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) { srs_error("start to play stream failed. ret=%d", ret); return ret; } if ((ret = http_hooks_on_play()) != ERROR_SUCCESS) { srs_error("http hook on_play failed. ret=%d", ret); return ret; } srs_info("start to play stream %s success", req->stream.c_str()); ret = playing(source); http_hooks_on_stop(); return ret; } case SrsRtmpConnFMLEPublish: { srs_verbose("FMLE start to publish stream %s.", req->stream.c_str()); if (vhost_is_edge) { if ((ret = source->on_edge_start_publish()) != ERROR_SUCCESS) { srs_error("notice edge start publish stream failed. ret=%d", ret); return ret; } } if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) { srs_error("start to publish stream failed. ret=%d", ret); return ret; } if ((ret = http_hooks_on_publish()) != ERROR_SUCCESS) { srs_error("http hook on_publish failed. ret=%d", ret); return ret; } srs_info("start to publish stream %s success", req->stream.c_str()); ret = fmle_publish(source); // when edge, notice edge to change state. // when origin, notice all service to unpublish. if (vhost_is_edge) { source->on_edge_proxy_unpublish(); } else { source->on_unpublish(); } http_hooks_on_unpublish(); return ret; } case SrsRtmpConnFlashPublish: { srs_verbose("flash start to publish stream %s.", req->stream.c_str()); if (vhost_is_edge) { if ((ret = source->on_edge_start_publish()) != ERROR_SUCCESS) { srs_error("notice edge start publish stream failed. ret=%d", ret); return ret; } } if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) { srs_error("flash start to publish stream failed. ret=%d", ret); return ret; } if ((ret = http_hooks_on_publish()) != ERROR_SUCCESS) { srs_error("http hook on_publish failed. ret=%d", ret); return ret; } srs_info("flash start to publish stream %s success", req->stream.c_str()); ret = flash_publish(source); // when edge, notice edge to change state. // when origin, notice all service to unpublish. if (vhost_is_edge) { source->on_edge_proxy_unpublish(); } else { source->on_unpublish(); } http_hooks_on_unpublish(); return ret; } default: { ret = ERROR_SYSTEM_CLIENT_INVALID; srs_info("invalid client type=%d. ret=%d", type, ret); return ret; } } return ret; }
int SrsRtmpConn::stream_service_cycle() { int ret = ERROR_SUCCESS; SrsRtmpConnType type; if ((ret = rtmp->identify_client(res->stream_id, type, req->stream, req->duration)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("identify client failed. ret=%d", ret); } return ret; } req->strip(); srs_trace("client identified, type=%s, stream_name=%s, duration=%.2f", srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration); // client is identified, set the timeout to service timeout. rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US); rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US); // set chunk size to larger. int chunk_size = _srs_config->get_chunk_size(req->vhost); if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) { srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret); return ret; } srs_info("set chunk_size=%d success", chunk_size); // do token traverse before serve it. bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost); bool edge_traverse = _srs_config->get_vhost_edge_token_traverse(req->vhost); if (vhost_is_edge && edge_traverse) { if ((ret = check_edge_token_traverse_auth()) != ERROR_SUCCESS) { srs_warn("token auth failed, ret=%d", ret); return ret; } } // find a source to serve. SrsSource* source = NULL; if ((ret = SrsSource::find(req, &source)) != ERROR_SUCCESS) { return ret; } srs_assert(source != NULL); // check ASAP, to fail it faster if invalid. if (type != SrsRtmpConnPlay && !vhost_is_edge) { // check publish available // for edge, never check it, for edge use proxy mode. if (!source->can_publish()) { ret = ERROR_SYSTEM_STREAM_BUSY; srs_warn("stream %s is already publishing. ret=%d", req->get_stream_url().c_str(), ret); // to delay request st_usleep(SRS_STREAM_BUSY_SLEEP_US); return ret; } } bool enabled_cache = _srs_config->get_gop_cache(req->vhost); srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%d[%d]", req->get_stream_url().c_str(), ip.c_str(), enabled_cache, vhost_is_edge, source->source_id(), source->source_id()); source->set_cache(enabled_cache); switch (type) { case SrsRtmpConnPlay: { srs_verbose("start to play stream %s.", req->stream.c_str()); if (vhost_is_edge) { // notice edge to start for the first client. if ((ret = source->on_edge_start_play()) != ERROR_SUCCESS) { srs_error("notice edge start play stream failed. ret=%d", ret); return ret; } } // response connection start play if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) { srs_error("start to play stream failed. ret=%d", ret); return ret; } if ((ret = http_hooks_on_play()) != ERROR_SUCCESS) { srs_error("http hook on_play failed. ret=%d", ret); return ret; } srs_info("start to play stream %s success", req->stream.c_str()); ret = playing(source); http_hooks_on_stop(); return ret; } case SrsRtmpConnFMLEPublish: { srs_verbose("FMLE start to publish stream %s.", req->stream.c_str()); if (vhost_is_edge) { if ((ret = source->on_edge_start_publish()) != ERROR_SUCCESS) { srs_error("notice edge start publish stream failed. ret=%d", ret); return ret; } } if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) { srs_error("start to publish stream failed. ret=%d", ret); return ret; } if (!vhost_is_edge) { if ((ret = source->acquire_publish()) != ERROR_SUCCESS) { return ret; } } ret = fmle_publishing(source); if (!vhost_is_edge) { source->release_publish(); } return ret; } case SrsRtmpConnFlashPublish: { srs_verbose("flash start to publish stream %s.", req->stream.c_str()); if (vhost_is_edge) { if ((ret = source->on_edge_start_publish()) != ERROR_SUCCESS) { srs_error("notice edge start publish stream failed. ret=%d", ret); return ret; } } if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) { srs_error("flash start to publish stream failed. ret=%d", ret); return ret; } if (!vhost_is_edge) { if ((ret = source->acquire_publish()) != ERROR_SUCCESS) { return ret; } } ret = flash_publishing(source); if (!vhost_is_edge) { source->release_publish(); } return ret; } default: { ret = ERROR_SYSTEM_CLIENT_INVALID; srs_info("invalid client type=%d. ret=%d", type, ret); return ret; } } return ret; }
int SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) { int ret = ERROR_SUCCESS; // when handler not the root, we think the handler is ok. ISrsHttpHandler* h = *ph? *ph : NULL; if (h && h->entry && h->entry->pattern != "/") { return ret; } // only hijack for http streaming, http-flv/ts/mp3/aac. std::string ext = request->ext(); if (ext.empty()) { return ret; } // find the actually request vhost. SrsConfDirective* vhost = _srs_config->get_vhost(request->host()); if (!vhost || !_srs_config->get_vhost_enabled(vhost)) { return ret; } // find the entry template for the stream. SrsLiveEntry* entry = NULL; if (true) { // no http streaming on vhost, ignore. std::map<std::string, SrsLiveEntry*>::iterator it = tflvs.find(vhost->arg0()); if (it == tflvs.end()) { return ret; } // hstrs not enabled, ignore. // for origin, the http stream will be mount already when publish, // so it must never enter this line for stream already mounted. // for edge, the http stream is trigger by hstrs and mount by it, // so we only hijack when only edge and hstrs is on. entry = it->second; if (!entry->hstrs) { return ret; } // check entry and request extension. if (entry->is_flv()) { if (ext != ".flv") { return ret; } } else if (entry->is_ts()) { if (ext != ".ts") { return ret; } } else if (entry->is_mp3()) { if (ext != ".mp3") { return ret; } } else if (entry->is_aac()) { if (ext != ".aac") { return ret; } } else { return ret; } } // convert to concreate class. SrsHttpMessage* hreq = dynamic_cast<SrsHttpMessage*>(request); srs_assert(hreq); // hijack for entry. SrsRequest* r = hreq->to_request(vhost->arg0()); SrsAutoFree(SrsRequest, r); std::string sid = r->get_stream_url(); // check whether the http remux is enabled, // for example, user disable the http flv then reload. if (sflvs.find(sid) != sflvs.end()) { SrsLiveEntry* s_entry = sflvs[sid]; if (!s_entry->stream->entry->enabled) { // only when the http entry is disabled, check the config whether http flv disable, // for the http flv edge use hijack to trigger the edge ingester, we always mount it // eventhough the origin does not exists the specified stream. if (!_srs_config->get_vhost_http_remux_enabled(r->vhost)) { srs_error("stream is disabled, hijack failed. ret=%d", ret); return ret; } } } SrsSource* s = SrsSource::fetch(r); if (!s) { if ((ret = SrsSource::create(r, server, server, &s)) != ERROR_SUCCESS) { return ret; } } srs_assert(s != NULL); // create http streaming handler. if ((ret = http_mount(s, r)) != ERROR_SUCCESS) { return ret; } // use the handler if exists. if (ph) { if (sflvs.find(sid) != sflvs.end()) { entry = sflvs[sid]; *ph = entry->stream; } } // trigger edge to fetch from origin. bool vhost_is_edge = _srs_config->get_vhost_is_edge(r->vhost); srs_trace("hstrs: source url=%s, is_edge=%d, source_id=%d[%d]", r->get_stream_url().c_str(), vhost_is_edge, s->source_id(), s->source_id()); return ret; }