+// H.264 video comes out in encoding order (e.g. with two B-frames:
+// 0, 3, 1, 2, 6, 4, 5, etc.), but uncompressed video needs to
+// come in the right order. Since we do everything, including waiting
+// for the frames to come out of OpenGL, in encoding order, we need
+// a reordering buffer for uncompressed frames so that they come out
+// correctly. We go the super-lazy way of not making it understand
+// anything about the true order (which introduces some extra latency,
+// though); we know that for N B-frames we need at most (N-1) frames
+// in the reorder buffer, and can just sort on that.
+//
+// The class also deals with keeping a freelist as needed.
+class FrameReorderer {
+public:
+ FrameReorderer(unsigned queue_length, int width, int height);
+
+ // Returns the next frame to insert with its pts, if any. Otherwise -1 and nullptr.
+ // Does _not_ take ownership of data; a copy is taken if needed.
+ // The returned pointer is valid until the next call to reorder_frame, or destruction.
+ // As a special case, if queue_length == 0, will just return pts and data (no reordering needed).
+ pair<int64_t, const uint8_t *> reorder_frame(int64_t pts, const uint8_t *data);
+
+ // The same as reorder_frame, but without inserting anything. Used to empty the queue.
+ pair<int64_t, const uint8_t *> get_first_frame();
+
+ bool empty() const { return frames.empty(); }
+
+private:
+ unsigned queue_length;
+ int width, height;
+
+ priority_queue<pair<int64_t, uint8_t *>> frames;
+ stack<uint8_t *> freelist; // Includes the last value returned from reorder_frame.
+
+ // Owns all the pointers. Normally, freelist and frames could do this themselves,
+ // except priority_queue doesn't work well with movable-only types.
+ vector<unique_ptr<uint8_t[]>> owner;
+};
+
+FrameReorderer::FrameReorderer(unsigned queue_length, int width, int height)
+ : queue_length(queue_length), width(width), height(height)
+{
+ for (unsigned i = 0; i < queue_length; ++i) {
+ owner.emplace_back(new uint8_t[width * height * 2]);
+ freelist.push(owner.back().get());
+ }
+}
+
+pair<int64_t, const uint8_t *> FrameReorderer::reorder_frame(int64_t pts, const uint8_t *data)
+{
+ if (queue_length == 0) {
+ return make_pair(pts, data);
+ }
+
+ assert(!freelist.empty());
+ uint8_t *storage = freelist.top();
+ freelist.pop();
+ memcpy(storage, data, width * height * 2);
+ frames.emplace(-pts, storage); // Invert pts to get smallest first.
+
+ if (frames.size() >= queue_length) {
+ return get_first_frame();
+ } else {
+ return make_pair(-1, nullptr);
+ }
+}
+
+pair<int64_t, const uint8_t *> FrameReorderer::get_first_frame()
+{
+ assert(!frames.empty());
+ pair<int64_t, uint8_t *> storage = frames.top();
+ frames.pop();
+ int64_t pts = storage.first;
+ freelist.push(storage.second);
+ return make_pair(-pts, storage.second); // Re-invert pts (see reorder_frame()).
+}
+