X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=httpd.cpp;h=fff3467e99617c0b4d211f0dd86fae93399a6a67;hb=ec67a4b205b93f3d260470485949b29abac6ad21;hp=638c9853dde735e6282030aac990f2481841e6de;hpb=df2284056394481ce5d5216f774d4a146b4983eb;p=nageru diff --git a/httpd.cpp b/httpd.cpp index 638c985..fff3467 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -10,6 +10,9 @@ #include #include #include +extern "C" { +#include +} #include "defs.h" #include "metacube2.h" @@ -22,18 +25,13 @@ using namespace std; HTTPD::HTTPD() { - global_metrics.register_int_metric("num_connected_clients", &metric_num_connected_clients, Metrics::TYPE_GAUGE); + global_metrics.add("num_connected_clients", &metric_num_connected_clients, Metrics::TYPE_GAUGE); + global_metrics.add("num_connected_multicam_clients", &metric_num_connected_multicam_clients, Metrics::TYPE_GAUGE); } HTTPD::~HTTPD() { - if (mhd) { - MHD_quiesce_daemon(mhd); - for (Stream *stream : streams) { - stream->stop(); - } - MHD_stop_daemon(mhd); - } + stop(); } void HTTPD::start(int port) @@ -49,11 +47,25 @@ void HTTPD::start(int port) } } -void HTTPD::add_data(const char *buf, size_t size, bool keyframe) +void HTTPD::stop() +{ + if (mhd) { + MHD_quiesce_daemon(mhd); + for (Stream *stream : streams) { + stream->stop(); + } + MHD_stop_daemon(mhd); + mhd = nullptr; + } +} + +void HTTPD::add_data(StreamType stream_type, const char *buf, size_t size, bool keyframe, int64_t time, AVRational timebase) { unique_lock lock(streams_mutex); for (Stream *stream : streams) { - stream->add_data(buf, size, keyframe ? Stream::DATA_TYPE_KEYFRAME : Stream::DATA_TYPE_OTHER); + if (stream->get_stream_type() == stream_type) { + stream->add_data(buf, size, keyframe ? Stream::DATA_TYPE_KEYFRAME : Stream::DATA_TYPE_OTHER, time, timebase); + } } } @@ -78,6 +90,12 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, } else { framing = HTTPD::Stream::FRAMING_RAW; } + HTTPD::StreamType stream_type; + if (strcmp(url, "/multicam.mp4") == 0) { + stream_type = HTTPD::StreamType::MULTICAM_STREAM; + } else { + stream_type = HTTPD::StreamType::MAIN_STREAM; + } if (strcmp(url, "/metrics") == 0) { string contents = global_metrics.serialize(); @@ -88,14 +106,40 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. return ret; } + if (endpoints.count(url)) { + pair contents_and_type = endpoints[url].callback(); + MHD_Response *response = MHD_create_response_from_buffer( + contents_and_type.first.size(), &contents_and_type.first[0], MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(response, "Content-type", contents_and_type.second.c_str()); + if (endpoints[url].cors_policy == ALLOW_ALL_ORIGINS) { + MHD_add_response_header(response, "Access-Control-Allow-Origin", "*"); + } + int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. + return ret; + } + + // Small hack; reject unknown /channels/foo. + if (string(url).find("/channels/") == 0) { + string contents = "Not found."; + MHD_Response *response = MHD_create_response_from_buffer( + contents.size(), &contents[0], MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(response, "Content-type", "text/plain"); + int ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. + return ret; + } - HTTPD::Stream *stream = new HTTPD::Stream(this, framing); - stream->add_data(header.data(), header.size(), Stream::DATA_TYPE_HEADER); + HTTPD::Stream *stream = new HTTPD::Stream(this, framing, stream_type); + stream->add_data(header[stream_type].data(), header[stream_type].size(), Stream::DATA_TYPE_HEADER, AV_NOPTS_VALUE, AVRational{ 1, 0 }); { unique_lock lock(streams_mutex); streams.insert(stream); } ++metric_num_connected_clients; + if (stream_type == HTTPD::StreamType::MULTICAM_STREAM) { + ++metric_num_connected_multicam_clients; + } *con_cls = stream; // Does not strictly have to be equal to MUX_BUFFER_SIZE. @@ -116,6 +160,9 @@ void HTTPD::free_stream(void *cls) { HTTPD::Stream *stream = (HTTPD::Stream *)cls; HTTPD *httpd = stream->get_parent(); + if (stream->get_stream_type() == HTTPD::StreamType::MULTICAM_STREAM) { + --httpd->metric_num_connected_multicam_clients; + } { unique_lock lock(httpd->streams_mutex); delete stream; @@ -164,7 +211,7 @@ ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max) return ret; } -void HTTPD::Stream::add_data(const char *buf, size_t buf_size, HTTPD::Stream::DataType data_type) +void HTTPD::Stream::add_data(const char *buf, size_t buf_size, HTTPD::Stream::DataType data_type, int64_t time, AVRational timebase) { if (buf_size == 0) { return; @@ -179,15 +226,34 @@ void HTTPD::Stream::add_data(const char *buf, size_t buf_size, HTTPD::Stream::Da unique_lock lock(buffer_mutex); if (framing == FRAMING_METACUBE) { - metacube2_block_header hdr; - memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); - hdr.size = htonl(buf_size); int flags = 0; if (data_type == DATA_TYPE_HEADER) { flags |= METACUBE_FLAGS_HEADER; } else if (data_type == DATA_TYPE_OTHER) { flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START; } + + // If we're about to send a keyframe, send a pts metadata block + // to mark its time. + if ((flags & METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START) == 0 && time != AV_NOPTS_VALUE) { + metacube2_pts_packet packet; + packet.type = htobe64(METACUBE_METADATA_TYPE_NEXT_BLOCK_PTS); + packet.pts = htobe64(time); + packet.timebase_num = htobe64(timebase.num); + packet.timebase_den = htobe64(timebase.den); + + metacube2_block_header hdr; + memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); + hdr.size = htonl(sizeof(packet)); + hdr.flags = htons(METACUBE_FLAGS_METADATA); + hdr.csum = htons(metacube2_compute_crc(&hdr)); + buffered_data.emplace_back((char *)&hdr, sizeof(hdr)); + buffered_data.emplace_back((char *)&packet, sizeof(packet)); + } + + metacube2_block_header hdr; + memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); + hdr.size = htonl(buf_size); hdr.flags = htons(flags); hdr.csum = htons(metacube2_compute_crc(&hdr)); buffered_data.emplace_back((char *)&hdr, sizeof(hdr));