From 5743f5528c75acdfd381a494db0b4936a3ab218b Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 5 Apr 2018 00:48:13 +0200 Subject: [PATCH] In Metacube, mark each keyframe with a pts metadata block (for easier HLS segmentation). --- bmusb | 2 +- httpd.cpp | 36 +++++++++++++++++++++++++++++------- httpd.h | 8 ++++++-- kaeru.cpp | 2 +- metacube2.h | 22 +++++++++++++++++++--- video_encoder.cpp | 2 +- 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/bmusb b/bmusb index e287f28..6a012a4 160000 --- a/bmusb +++ b/bmusb @@ -1 +1 @@ -Subproject commit e287f28087aef9cfd6aa47acd0d283bc177a9d70 +Subproject commit 6a012a41c5422092cdac1f18a9019f37c0b85368 diff --git a/httpd.cpp b/httpd.cpp index f82c803..9782dca 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -10,6 +10,9 @@ #include #include #include +extern "C" { +#include +} #include "defs.h" #include "metacube2.h" @@ -49,11 +52,11 @@ void HTTPD::start(int port) } } -void HTTPD::add_data(const char *buf, size_t size, bool keyframe) +void HTTPD::add_data(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); + stream->add_data(buf, size, keyframe ? Stream::DATA_TYPE_KEYFRAME : Stream::DATA_TYPE_OTHER, time, timebase); } } @@ -113,7 +116,7 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, } HTTPD::Stream *stream = new HTTPD::Stream(this, framing); - stream->add_data(header.data(), header.size(), Stream::DATA_TYPE_HEADER); + stream->add_data(header.data(), header.size(), Stream::DATA_TYPE_HEADER, AV_NOPTS_VALUE, AVRational{ 1, 0 }); { unique_lock lock(streams_mutex); streams.insert(stream); @@ -187,7 +190,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; @@ -202,15 +205,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)); diff --git a/httpd.h b/httpd.h index 509ae01..1c3c98e 100644 --- a/httpd.h +++ b/httpd.h @@ -16,6 +16,10 @@ #include #include +extern "C" { +#include +} + struct MHD_Connection; struct MHD_Daemon; @@ -42,7 +46,7 @@ public: } void start(int port); - void add_data(const char *buf, size_t size, bool keyframe); + void add_data(const char *buf, size_t size, bool keyframe, int64_t time, AVRational timebase); int64_t get_num_connected_clients() const { return metric_num_connected_clients.load(); } @@ -77,7 +81,7 @@ private: DATA_TYPE_KEYFRAME, DATA_TYPE_OTHER }; - void add_data(const char *buf, size_t size, DataType data_type); + void add_data(const char *buf, size_t size, DataType data_type, int64_t time, AVRational timebase); void stop(); HTTPD *get_parent() const { return parent; } diff --git a/kaeru.cpp b/kaeru.cpp index 6d98cce..e763c34 100644 --- a/kaeru.cpp +++ b/kaeru.cpp @@ -50,7 +50,7 @@ int write_packet(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType ty stream_mux_header.append((char *)buf, buf_size); httpd->set_header(stream_mux_header); } else { - httpd->add_data((char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT); + httpd->add_data((char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT, time, AVRational{ AV_TIME_BASE, 1 }); } return buf_size; } diff --git a/metacube2.h b/metacube2.h index 5b6077e..4f232c8 100644 --- a/metacube2.h +++ b/metacube2.h @@ -35,9 +35,8 @@ struct metacube2_block_header { uint16_t metacube2_compute_crc(const struct metacube2_block_header *hdr); /* - * The only currently defined metadata type. Set by the encoder, - * and can be measured for latency purposes (e.g., if the network - * can't keep up, the latency will tend to increase. + * Set by the encoder, and can be measured for latency purposes (e.g., if the + * network can't keep up, the latency will tend to increase. */ #define METACUBE_METADATA_TYPE_ENCODER_TIMESTAMP 0x1 @@ -52,4 +51,21 @@ struct metacube2_timestamp_packet { uint64_t tv_nsec; }; +/* + * Sent before a block to mark its presentation timestamp (ie., counts + * only for the next Metacube block). Used so that the reflector can know + * the length (in seconds) of fragments. + */ +#define METACUBE_METADATA_TYPE_NEXT_BLOCK_PTS 0x2 + +struct metacube2_pts_packet { + uint64_t type; /* METACUBE_METADATA_TYPE_NEXT_BLOCK_PTS, in network byte order. */ + + /* The timestamp of the first packet in the next block, in network byte order. */ + int64_t pts; + + /* Timebase "pts" is expressed in, as a fraction. Network byte order. */ + uint64_t timebase_num, timebase_den; +}; + #endif /* !defined(_METACUBE_H) */ diff --git a/video_encoder.cpp b/video_encoder.cpp index efc9732..d2f5dbe 100644 --- a/video_encoder.cpp +++ b/video_encoder.cpp @@ -219,7 +219,7 @@ int VideoEncoder::write_packet2(uint8_t *buf, int buf_size, AVIODataMarkerType t stream_mux_header.append((char *)buf, buf_size); httpd->set_header(stream_mux_header); } else { - httpd->add_data((char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT); + httpd->add_data((char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT, time, AVRational{ AV_TIME_BASE, 1 }); } return buf_size; } -- 2.39.2