int SrsRtmpConn::playing(SrsSource* source) { int ret = ERROR_SUCCESS; if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_play(req->vhost))) != ERROR_SUCCESS) { srs_error("check play_refer failed. ret=%d", ret); return ret; } srs_verbose("check play_refer success."); SrsConsumer* consumer = NULL; if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) { srs_error("create consumer failed. ret=%d", ret); return ret; } srs_assert(consumer != NULL); SrsAutoFree(SrsConsumer, consumer); srs_verbose("consumer created success."); rtmp->set_recv_timeout(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_CONSTS_STAGE_PLAY_USER); SrsSharedPtrMessageArray msgs(SYS_MAX_PLAY_SEND_MSGS); bool user_specified_duration_to_stop = (req->duration > 0); int64_t starttime = -1; while (true) { // collect elapse for pithy print. pithy_print.elapse(); // read from client. if (true) { SrsMessage* msg = NULL; ret = rtmp->recv_message(&msg); srs_verbose("play loop recv message. ret=%d", ret); if (ret == ERROR_SOCKET_TIMEOUT) { // it's ok, do nothing. ret = ERROR_SUCCESS; } else if (ret != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("recv client control message failed. ret=%d", ret); } return ret; } else { if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { if (!srs_is_system_control_error(ret)) { srs_error("process play control message failed. ret=%d", ret); } return ret; } } } // get messages from consumer. int count = 0; if ((ret = consumer->dump_packets(msgs.size, msgs.msgs, count)) != ERROR_SUCCESS) { srs_error("get messages from consumer failed. ret=%d", ret); return ret; } // reportable if (pithy_print.can_print()) { kbps->sample(); srs_trace("-> "SRS_CONSTS_LOG_PLAY " time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", pithy_print.age(), count, kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(), kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m()); } // sendout messages // @remark, becareful, all msgs must be free explicitly, // free by send_and_free_message or srs_freep. for (int i = 0; i < count; i++) { SrsSharedPtrMessage* msg = msgs.msgs[i]; // the send_message will free the msg, // so set the msgs[i] to NULL. msgs.msgs[i] = NULL; // only when user specifies the duration, // we start to collect the durations for each message. if (user_specified_duration_to_stop) { // foreach msg, collect the duration. // @remark: never use msg when sent it, for the protocol sdk will free it. if (starttime < 0 || starttime > msg->header.timestamp) { starttime = msg->header.timestamp; } duration += msg->header.timestamp - starttime; starttime = msg->header.timestamp; } // no need to assert msg, for the rtmp will assert it. if ((ret = rtmp->send_and_free_message(msg, res->stream_id)) != ERROR_SUCCESS) { srs_error("send message to client failed. ret=%d", ret); return ret; } } // if duration specified, and exceed it, stop play live. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/45 if (user_specified_duration_to_stop) { if (duration >= (int64_t)req->duration) { ret = ERROR_RTMP_DURATION_EXCEED; srs_trace("stop live for duration exceed. ret=%d", ret); return ret; } } // switch to other threads, to anti dead loop. st_usleep(0); } return ret; }
int SrsBufferCache::cycle() { int ret = ERROR_SUCCESS; // TODO: FIXME: support reload. if (fast_cache <= 0) { st_sleep(SRS_STREAM_CACHE_CYCLE_SECONDS); return ret; } // the stream cache will create consumer to cache stream, // which will trigger to fetch stream from origin for edge. SrsConsumer* consumer = NULL; if ((ret = source->create_consumer(NULL, consumer, false, false, true)) != ERROR_SUCCESS) { srs_error("http: create consumer failed. ret=%d", ret); return ret; } SrsAutoFree(SrsConsumer, consumer); SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream_cache(); SrsAutoFree(SrsPithyPrint, pprint); SrsMessageArray msgs(SRS_PERF_MW_MSGS); // set the queue size, which used for max cache. // TODO: FIXME: support reload. queue->set_queue_size(fast_cache); while (true) { pprint->elapse(); // 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) { srs_error("http: get messages from consumer failed. ret=%d", ret); return ret; } if (count <= 0) { srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // directly use sleep, donot use consumer wait. st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // ignore when nothing got. continue; } if (pprint->can_print()) { srs_trace("-> "SRS_CONSTS_LOG_HTTP_STREAM_CACHE" http: got %d msgs, age=%d, min=%d, mw=%d", count, pprint->age(), SRS_PERF_MW_MIN_MSGS, SRS_CONSTS_RTMP_PULSE_TIMEOUT_US / 1000); } // free the messages. for (int i = 0; i < count; i++) { SrsSharedPtrMessage* msg = msgs.msgs[i]; queue->enqueue(msg); } } return ret; }
int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_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 { ret = ERROR_HTTP_LIVE_STREAM_EXT; srs_error("http: unsupported pattern %s", entry->pattern.c_str()); return ret; } 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) { srs_error("http: create consumer failed. ret=%d", ret); return ret; } 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) { srs_error("stat client failed. ret=%d", ret); return ret; } // the memory writer. SrsBufferWriter writer(w); if ((ret = enc->initialize(&writer, cache)) != ERROR_SUCCESS) { srs_error("http: initialize stream encoder failed. ret=%d", ret); return ret; } // if gop cache enabled for encoder, dump to consumer. if (enc->has_cache()) { if ((ret = enc->dump_cache(consumer, source->jitter())) != ERROR_SUCCESS) { srs_error("http: dump cache to consumer failed. ret=%d", ret); return ret; } } #ifdef SRS_PERF_FAST_FLV_ENCODER SrsFastFlvStreamEncoder* ffe = dynamic_cast<SrsFastFlvStreamEncoder*>(enc); #endif // TODO: free and erase the disabled entry after all related connections is closed. while (entry->enabled) { pprint->elapse(); // 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) { srs_error("http: get messages from consumer failed. ret=%d", ret); return ret; } if (count <= 0) { srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // directly use sleep, donot use consumer wait. st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // 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_TIMEOUT_US / 1000); } // 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) { if (!srs_is_client_gracefully_close(ret)) { srs_error("http: send messages to client failed. ret=%d", ret); } return ret; } } return ret; }
int SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; ISrsStreamEncoder* 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 bool realtime = _srs_config->get_realtime_enabled(req->vhost); if (realtime) { enc = new SrsFlvStreamEncoder(); } else { 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 { ret = ERROR_HTTP_LIVE_STREAM_EXT; srs_error("http: unsupported pattern %s", entry->pattern.c_str()); return ret; } SrsAutoFree(ISrsStreamEncoder, 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) { srs_error("http: create consumer failed. ret=%d", ret); return ret; } SrsAutoFree(SrsConsumer, consumer); srs_verbose("http: consumer created success."); SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream(); SrsAutoFree(SrsPithyPrint, pprint); SrsMessageArray msgs(SRS_PERF_MW_MSGS); // the memory writer. SrsStreamWriter writer(w); if ((ret = enc->initialize(&writer, cache)) != ERROR_SUCCESS) { srs_error("http: initialize stream encoder failed. ret=%d", ret); return ret; } // if gop cache enabled for encoder, dump to consumer. if (enc->has_cache()) { if ((ret = enc->dump_cache(consumer, source->jitter())) != ERROR_SUCCESS) { srs_error("http: dump cache to consumer failed. ret=%d", ret); return ret; } } #ifdef SRS_PERF_FAST_FLV_ENCODER SrsFastFlvStreamEncoder* ffe = dynamic_cast<SrsFastFlvStreamEncoder*>(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<SrsHttpMessage*>(r); SrsResponseOnlyHttpConn* hc = dynamic_cast<SrsResponseOnlyHttpConn*>(hr->connection()); int mw_sleep = _srs_config->get_mw_sleep_ms(req->vhost); SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc); SrsAutoFree(SrsHttpRecvThread, trd); if ((ret = trd->start()) != ERROR_SUCCESS) { srs_error("http: start notify thread failed, ret=%d", ret); return ret; } // TODO: free and erase the disabled entry after all related connections is closed. while (entry->enabled) { pprint->elapse(); // Whether client closed the FD. if ((ret = trd->error_code()) != ERROR_SUCCESS) { return ret; } // 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) { srs_error("http: get messages from consumer failed. ret=%d", ret); return ret; } if (count <= 0) { srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // directly use sleep, donot use consumer wait. st_usleep(mw_sleep * 1000); // ignore when nothing got. continue; } if (pprint->can_print()) { srs_trace("-> "SRS_CONSTS_LOG_HTTP_STREAM" http: got %d msgs, age=%d, min=%d, mw=%d", count, pprint->age(), SRS_PERF_MW_MIN_MSGS, mw_sleep); } // 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) { if (!srs_is_client_gracefully_close(ret)) { srs_error("http: send messages to client failed. ret=%d", ret); } return ret; } } return ret; }