http-flv技术实现:
请求SRS返回的是:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: video/x-flv
Server: SRS/2.0.205
Transfer-Encoding: chunked
实现代码:
服务器启动时http端口的监听过程如下:
run_master()-->SrsServer::listen()--->SrsServer::listen_http_stream()
listen_http_stream服务端口监听流程如下:
srs_error_t SrsServer::listen_http_stream()
{
srs_error_t err = srs_success;
close_listeners(SrsListenerHttpStream);
if (_srs_config->get_http_stream_enabled()) {
SrsListener* listener = new SrsBufferListener(this, SrsListenerHttpStream);
listeners.push_back(listener);
std::string ep = _srs_config->get_http_stream_listen();
std::string ip;
int port;
srs_parse_endpoint(ep, ip, port);
if ((err = listener->listen(ip, port)) != srs_success) {
return srs_error_wrap(err, "http stream listen %s:%d", ip.c_str(), port);
}
}
return err;
}
1).判断是否开启HttpStream功能 _srs_config->get_http_stream_enabled()
2).创建SrsListener实例根据获取的ip的port启动监听listener->listen(ip, port)
3). SrsBufferListener::listen()中创建SrsTcpListener的实例并启动监听listener->listen()
4).SrsTcpListener::listen()中启动监听,创建SrsSTCoroutine协程实例,开启协程。
5).调用trd->start()函数后,最终会执行SrsTcpListener::cycle()。
具体调用过程请参照
http://blog.csdn.net/weixin_39799839/article/details/78579278
在cycle函数中会调用srs_accept阻塞等待客户端http请求的到来,
SrsTcpListener::cycle函数定义如下
当有http请求到来时srs_accept()返回连接的fd,接着调用handler->on_tcp_client()处理连理连接;
on_tcp_client接下来执行顺序为:handler->on_tcp_client()-->SrsBufferListener::on_tcp_client()-->SrsServer::accept_client。
最终由SrsServer::accept_client处理连接请求,具体代码如下:
SrsServer::accept_client调用fd2conn(type, stfd, &conn)创建一个连接对象SrsResponseOnlyHttpConn,SrsResponseOnlyHttpConn 继承者自 SrsHttpConn
srs_error_t SrsServer::fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnection** pconn)
{
......
if (type == SrsListenerRtmpStream) {
*pconn = new SrsRtmpConn(this, stfd, ip);
} else if (type == SrsListenerHttpApi) {
*pconn = new SrsHttpApi(this, stfd, http_api_mux, ip);
} else if (type == SrsListenerHttpStream) {
*pconn = new SrsResponseOnlyHttpConn(this, stfd, http_server, ip);
} else {
srs_warn("close for no service handler. fd=%d, ip=%s", fd, ip.c_str());
srs_close_stfd(stfd);
return err;
}
return err;
}
SrsHttpConn对应一个协程,conn.start()会启动该协程,进入到SrsHttpConn::do_cycle()循环来处理http请求:
调用流程如下:
SrsServer::accept_client--->SrsConnection::start--->SrsSTCoroutine::start-->SrsConnection::cycle--->SrsHttpConn::do_cycle
srs_error_t SrsHttpConn::do_cycle()
{
int ret = ERROR_SUCCESS;
srs_error_t err = srs_success;
srs_trace("HTTP client ip=%s", ip.c_str());
// initialize parser
if ((ret = parser->initialize(HTTP_REQUEST, false)) != ERROR_SUCCESS) {
return srs_error_new(ret, "init parser");
}
// set the recv timeout, for some clients never disconnect the connection.
// @see https://github.com/ossrs/srs/issues/398
skt->set_recv_timeout(SRS_HTTP_RECV_TMMS);
SrsRequest* last_req = NULL;
SrsAutoFree(SrsRequest, last_req);
// initialize the cors, which will proxy to mux.
bool crossdomain_enabled = _srs_config->get_http_stream_crossdomain();
if ((err = cors->initialize(http_mux, crossdomain_enabled)) != srs_success) {
return srs_error_wrap(err, "init cors");
}
// process http messages.
while ((err = trd->pull()) == srs_success) {
ISrsHttpMessage* req = NULL;
// get a http message
if ((ret = parser->parse_message(skt, this, &req)) != ERROR_SUCCESS) {
break;
}
// if SUCCESS, always NOT-NULL.
srs_assert(req);
// always free it in this scope.
SrsAutoFree(ISrsHttpMessage, req);
// copy request to last request object.
srs_freep(last_req);
SrsHttpMessage* hreq = dynamic_cast(req);
last_req = hreq->to_request(hreq->host());
// may should discard the body.
if ((err = on_got_http_message(req)) != srs_success) {
break;
}
// ok, handle http request.
SrsHttpResponseWriter writer(skt);
if ((err = process_request(&writer, req)) != srs_success) {
break;
}
// donot keep alive, disconnect it.
// @see https://github.com/ossrs/srs/issues/399
if (!req->is_keep_alive()) {
break;
}
}
srs_error_t r0 = srs_success;
if ((r0 = on_disconnect(last_req)) != srs_success) {
err = srs_error_wrap(err, "on disconnect %s", srs_error_desc(r0).c_str());
srs_freep(r0);
}
return err;
}
,函数SrsResponseOnlyHttpConn::on_got_http_message主要是用来读取第一次http请求中的消息内容。后面该连接的消息处理会交给SrsHttpRecvThread。
srs_error_t SrsResponseOnlyHttpConn::on_got_http_message(ISrsHttpMessage* msg)
{
int ret = ERROR_SUCCESS;
srs_error_t err = srs_success;
ISrsHttpResponseReader* br = msg->body_reader();
// when not specified the content length, ignore.
if (msg->content_length() == -1) {
return err;
}
// drop all request body.
while (!br->eof()) {
char body[4096];
if ((ret = br->read(body, 4096, NULL)) != ERROR_SUCCESS) {
return srs_error_new(ret, "read response");
}
}
return err;
}
SrsHttpConn::do_cycle中读取完http请求后,接着调用请求处理函数process_request
srs_error_t SrsHttpConn::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
srs_error_t err = srs_success;
srs_trace("HTTP %s %s, content-length=%" PRId64 "",
r->method_str().c_str(), r->url().c_str(), r->content_length());
// use cors server mux to serve http request, which will proxy to http_remux.
if ((err = cors->serve_http(w, r)) != srs_success) {
return srs_error_wrap(err, "mux serve");
}
return err;
}
函数SrsHttpConn::process_request调用cors->serve_http(w, r)对请求处理。cors是SrsHttpConn的成员变量,类型为SrsHttpCorsMux
srs_error_t SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
// If CORS enabled, and there is a "Origin" header, it's CORS.
if (enabled) {
for (int i = 0; i < r->request_header_count(); i++) {
string k = r->request_header_key_at(i);
if (k == "Origin" || k == "origin") {
required = true;
break;
}
}
}
// When CORS required, set the CORS headers.
if (required) {
SrsHttpHeader* h = w->header();
h->set("Access-Control-Allow-Origin", "*");
h->set("Access-Control-Allow-Methods", "GET, POST, HEAD, PUT, DELETE, OPTIONS");
h->set("Access-Control-Expose-Headers", "Server,range,Content-Length,Content-Range");
h->set("Access-Control-Allow-Headers", "origin,range,accept-encoding,referer,Cache-Control,X-Proxy-Authorization,X-Requested-With,Content-Type");
}
// handle the http options.
if (r->is_http_options()) {
w->header()->set_content_length(0);
if (enabled) {
w->write_header(SRS_CONSTS_HTTP_OK);
} else {
w->write_header(SRS_CONSTS_HTTP_MethodNotAllowed);
}
if ((ret = w->final_request()) != ERROR_SUCCESS) {
return srs_error_new(ret, "final request");
}
}
srs_assert(next);
return next->serve_http(w, r);
}
SrsHttpCorsMux::serve_http首选写入一些响应头部,接着调用next->serve_http,该方法为ISrsHttpServeMux类的
纯虚函数,next实际上是SrsHttpServer* SrsServer::http_server。赋值过程如下:
fd2conn()-->new SrsResponseOnlyHttpConn(this, stfd, http_server, ip)-->SrsHttpConn(this, stdfd, http_server, ip)
-->SrsHttpConn(this, stdfd, http_server, ip)-->SrsHttpConn::http_mux = http_server;
SrsHttpConn::do_cycle()调用cors->initialize(http_mux, crossdomain_enabled)完成初始化。
cors->initialize函数中会给next赋值,next的实际就是SrsServer::http_server
srs_error_t SrsHttpCorsMux::initialize(ISrsHttpServeMux* worker, bool cros_enabled)
{
next = worker;
enabled = cros_enabled;
return srs_success;
}
调用
cors->serve_http(w, r)实际就是调用SrsHttpServer::serve_http,具体代码如下
srs_error_t SrsHttpServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
srs_error_t err = srs_success;
// try http stream first.
ISrsHttpHandler* h = NULL;
if ((err = http_stream->mux.find_handler(r, &h)) != srs_success) {
return srs_error_wrap(err, "find handler");
}
if (!h->is_not_found()) {
return http_stream->mux.serve_http(w, r);
}
return http_static->mux.serve_http(w, r);
}
srs_error_t SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
srs_error_t err = srs_success;
ISrsHttpHandler* h = NULL;
if ((err = find_handler(r, &h)) != srs_success) {
return srs_error_wrap(err, "find handler");
}
srs_assert(h);
if ((err = h->serve_http(w, r)) != srs_success) {
return srs_error_wrap(err, "serve http");
}
return err;
}
SrsLiveStream与url的建立关联过程如下。
serve_http调用find_handler 然后会调用SrsHttpStreamServer::hijack接着调用SrsHttpStreamServer::http_mount,
http_mount函数中首先根据请求调用SrsSource::fetch_or_create创建或者获取一个SrsSource对象用来从源端获取流数据,
接着根据url判断是否已经创建了handler,如果没有创建一个新的,如果有返回已经存在的。
int SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r)
{
......
// remove the default vhost mount
mount = srs_string_replace(mount, SRS_CONSTS_RTMP_DEFAULT_VHOST"/", "/");
entry = new SrsLiveEntry(mount);
entry->cache = new SrsBufferCache(s, r);
entry->stream = new SrsLiveStream(s, r, entry->cache);
......
}
srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
srs_error_t err = srs_success;
ISrsBufferEncoder* enc = NULL;
srs_assert(entry);
if (srs_string_ends_with(entry->pattern, ".flv")) {
w->header()->set_content_type("video/x-flv");
#ifdef SRS_PERF_FAST_FLV_ENCODER
enc = new SrsFastFlvStreamEncoder();
#else
enc = new SrsFlvStreamEncoder();
#endif
} else if (srs_string_ends_with(entry->pattern, ".aac")) {
w->header()->set_content_type("audio/x-aac");
enc = new SrsAacStreamEncoder();
} else if (srs_string_ends_with(entry->pattern, ".mp3")) {
w->header()->set_content_type("audio/mpeg");
enc = new SrsMp3StreamEncoder();
} else if (srs_string_ends_with(entry->pattern, ".ts")) {
w->header()->set_content_type("video/MP2T");
enc = new SrsTsStreamEncoder();
} else {
return srs_error_new(ERROR_HTTP_LIVE_STREAM_EXT, "invalid pattern=%s", entry->pattern.c_str());
}
SrsAutoFree(ISrsBufferEncoder, enc);
// create consumer of souce, ignore gop cache, use the audio gop cache.
SrsConsumer* consumer = NULL;
if ((ret = source->create_consumer(NULL, consumer, true, true, !enc->has_cache())) != ERROR_SUCCESS) {
return srs_error_new(ret, "create consumer");
}
SrsAutoFree(SrsConsumer, consumer);
srs_verbose("http: consumer created success.");
SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream();
SrsAutoFree(SrsPithyPrint, pprint);
SrsMessageArray msgs(SRS_PERF_MW_MSGS);
// update the statistic when source disconveried.
SrsStatistic* stat = SrsStatistic::instance();
if ((ret = stat->on_client(_srs_context->get_id(), req, NULL, SrsRtmpConnPlay)) != ERROR_SUCCESS) {
return srs_error_new(ret, "stat on client");
}
// the memory writer.
SrsBufferWriter writer(w);
if ((ret = enc->initialize(&writer, cache)) != ERROR_SUCCESS) {
return srs_error_new(ret, "init encoder");
}
// if gop cache enabled for encoder, dump to consumer.
if (enc->has_cache()) {
if ((ret = enc->dump_cache(consumer, source->jitter())) != ERROR_SUCCESS) {
return srs_error_new(ret, "encoder dump cache");
}
}
#ifdef SRS_PERF_FAST_FLV_ENCODER
SrsFastFlvStreamEncoder* ffe = dynamic_cast(enc);
#endif
// Use receive thread to accept the close event to avoid FD leak.
// @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427
SrsHttpMessage* hr = dynamic_cast(r);
SrsResponseOnlyHttpConn* hc = dynamic_cast(hr->connection());
SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc);
SrsAutoFree(SrsHttpRecvThread, trd);
if ((err = trd->start()) != srs_success) {
return srs_error_wrap(err, "start recv thread");
}
// TODO: free and erase the disabled entry after all related connections is closed.
while (entry->enabled) {
pprint->elapse();
// Whether client closed the FD.
if ((err = trd->pull()) != srs_success) {
return srs_error_wrap(err, "recv thread");
}
// get messages from consumer.
// each msg in msgs.msgs must be free, for the SrsMessageArray never free them.
int count = 0;
if ((ret = consumer->dump_packets(&msgs, count)) != ERROR_SUCCESS) {
return srs_error_new(ret, "consumer dump packets");
}
if (count <= 0) {
srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TMMS);
// directly use sleep, donot use consumer wait.
srs_usleep(SRS_CONSTS_RTMP_PULSE_TMMS * 1000);
// ignore when nothing got.
continue;
}
if (pprint->can_print()) {
srs_info("-> " SRS_CONSTS_LOG_HTTP_STREAM " http: got %d msgs, age=%d, min=%d, mw=%d",
count, pprint->age(), SRS_PERF_MW_MIN_MSGS, SRS_CONSTS_RTMP_PULSE_TMMS);
}
// sendout all messages.
#ifdef SRS_PERF_FAST_FLV_ENCODER
if (ffe) {
ret = ffe->write_tags(msgs.msgs, count);
} else {
ret = streaming_send_messages(enc, msgs.msgs, count);
}
#else
ret = streaming_send_messages(enc, msgs.msgs, count);
#endif
// free the messages.
for (int i = 0; i < count; i++) {
SrsSharedPtrMessage* msg = msgs.msgs[i];
srs_freep(msg);
}
// check send error code.
if (ret != ERROR_SUCCESS) {
return srs_error_new(ret, "send messages");
}
}
return err;
}
首先该函数会调用source->create_consumer创建一个SrsConsumer对象,用来接收SrsSource中获取的源站数据。
然后该函数会开启一个新的协程来处理客户端的请求。
/**
* The HTTP receive thread, try to read messages util EOF.
* For example, the HTTP FLV serving thread will use the receive thread to break
* when client closed the request, to avoid FD leak.
SrsHttpMessage* hr = dynamic_cast(r);
SrsResponseOnlyHttpConn* hc = dynamic_cast(hr->connection());
SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc);
然后程序进入到循环中通过cosumer不断的从源站获取数据,再通过SrsFlvStreamEncoder::write_tags将拉取的数据写入http响应中。
当处理客户端的协程发现客户端关闭连接时,该循环退出