]> git.sesse.net Git - nageru/commitdiff
In Metacube, mark each keyframe with a pts metadata block (for easier HLS segmentation).
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 4 Apr 2018 22:48:13 +0000 (00:48 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 4 Apr 2018 22:48:22 +0000 (00:48 +0200)
bmusb
httpd.cpp
httpd.h
kaeru.cpp
metacube2.h
video_encoder.cpp

diff --git a/bmusb b/bmusb
index e287f28087aef9cfd6aa47acd0d283bc177a9d70..6a012a41c5422092cdac1f18a9019f37c0b85368 160000 (submodule)
--- a/bmusb
+++ b/bmusb
@@ -1 +1 @@
-Subproject commit e287f28087aef9cfd6aa47acd0d283bc177a9d70
+Subproject commit 6a012a41c5422092cdac1f18a9019f37c0b85368
index f82c8038e55f61b90031534dd88c540e17d53d7e..9782dca7ba7807ab45c0e1994cd6a25642623086 100644 (file)
--- a/httpd.cpp
+++ b/httpd.cpp
@@ -10,6 +10,9 @@
 #include <sys/time.h>
 #include <time.h>
 #include <memory>
+extern "C" {
+#include <libavutil/avutil.h>
+}
 
 #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<mutex> 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<mutex> 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<mutex> 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 509ae0146a344f101d34d199b36bcab3380c06a6..1c3c98ee3ba3e1d9bbe25f50e4831cc40e6e696e 100644 (file)
--- a/httpd.h
+++ b/httpd.h
 #include <unordered_map>
 #include <utility>
 
+extern "C" {
+#include <libavutil/rational.h>
+}
+
 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; }
 
index 6d98cce1791992a47df17663b77d5159169061fd..e763c3429383064c529d8d1e45c5f8450b75ec51 100644 (file)
--- 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;
 }
index 5b6077e8ae4d08d232cd8840f119c7ac5ac41f6b..4f232c8767046f0c2beab249596677dc2f74ce80 100644 (file)
@@ -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) */
index efc9732c654093f4a00240039b5de55527382862..d2f5dbe43d5a625583a3f9b60bf19f768a679a54 100644 (file)
@@ -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;
 }