if (!clip_ready) {
if (video_stream != nullptr) {
- video_stream->schedule_refresh_frame(steady_clock::now(), pts, /*display_func=*/nullptr);
+ video_stream->schedule_refresh_frame(steady_clock::now(), pts, /*display_func=*/nullptr, QueueSpotHolder());
}
continue;
}
});
if (new_clip_ready) {
if (video_stream != nullptr) {
+ lock.unlock(); // Urg.
video_stream->clear_queue();
- num_queued_frames = 0;
+ lock.lock();
}
goto wait_for_clip;
}
if (in_pts_lower == in_pts_upper) {
auto display_func = [this, primary_stream_idx, in_pts_lower, secondary_stream_idx, secondary_pts, fade_alpha]{
destination->setFrame(primary_stream_idx, in_pts_lower, /*interpolated=*/false, secondary_stream_idx, secondary_pts, fade_alpha);
- unique_lock<mutex> lock(queue_state_mu);
- assert(num_queued_frames > 0);
- --num_queued_frames;
- new_clip_changed.notify_all();
};
if (video_stream == nullptr) {
display_func();
} else {
if (secondary_stream_idx == -1) {
- video_stream->schedule_original_frame(next_frame_start, pts, display_func, primary_stream_idx, in_pts_lower);
- unique_lock<mutex> lock(queue_state_mu);
- ++num_queued_frames;
+ video_stream->schedule_original_frame(
+ next_frame_start, pts, display_func, QueueSpotHolder(this),
+ primary_stream_idx, in_pts_lower);
} else {
- bool ok = video_stream->schedule_faded_frame(next_frame_start, pts, display_func, primary_stream_idx, in_pts_lower, secondary_stream_idx, secondary_pts, fade_alpha);
- unique_lock<mutex> lock(queue_state_mu);
- if (ok) ++num_queued_frames;
+ video_stream->schedule_faded_frame(next_frame_start, pts, display_func,
+ QueueSpotHolder(this), primary_stream_idx, in_pts_lower,
+ secondary_stream_idx, secondary_pts, fade_alpha);
}
}
continue;
if (fabs(snap_pts_as_frameno - frameno) < 0.01) {
auto display_func = [this, primary_stream_idx, snap_pts, secondary_stream_idx, secondary_pts, fade_alpha]{
destination->setFrame(primary_stream_idx, snap_pts, /*interpolated=*/false, secondary_stream_idx, secondary_pts, fade_alpha);
- unique_lock<mutex> lock(queue_state_mu);
- assert(num_queued_frames > 0);
- --num_queued_frames;
- new_clip_changed.notify_all();
};
if (video_stream == nullptr) {
display_func();
} else {
if (secondary_stream_idx == -1) {
- video_stream->schedule_original_frame(next_frame_start, pts, display_func, primary_stream_idx, snap_pts);
- unique_lock<mutex> lock(queue_state_mu);
- ++num_queued_frames;
+ video_stream->schedule_original_frame(
+ next_frame_start, pts, display_func,
+ QueueSpotHolder(this), primary_stream_idx, snap_pts);
} else {
- bool ok = video_stream->schedule_faded_frame(next_frame_start, pts, display_func, primary_stream_idx, snap_pts, secondary_stream_idx, secondary_pts, fade_alpha);
- unique_lock<mutex> lock(queue_state_mu);
- if (ok) ++num_queued_frames;
+ video_stream->schedule_faded_frame(
+ next_frame_start, pts, display_func, QueueSpotHolder(this),
+ primary_stream_idx, snap_pts, secondary_stream_idx, secondary_pts, fade_alpha);
}
}
in_pts_origin += snap_pts - in_pts;
} else {
auto display_func = [this, primary_stream_idx, pts, secondary_stream_idx, secondary_pts, fade_alpha]{
destination->setFrame(primary_stream_idx, pts, /*interpolated=*/true, secondary_stream_idx, secondary_pts, fade_alpha);
- assert(num_queued_frames > 0);
- --num_queued_frames;
- new_clip_changed.notify_all();
};
- bool ok = video_stream->schedule_interpolated_frame(next_frame_start, pts, display_func, primary_stream_idx, in_pts_lower, in_pts_upper, alpha, secondary_stream_idx, secondary_pts, fade_alpha);
- unique_lock<mutex> lock(queue_state_mu);
- if (ok) ++num_queued_frames;
+ video_stream->schedule_interpolated_frame(
+ next_frame_start, pts, display_func, QueueSpotHolder(this),
+ primary_stream_idx, in_pts_lower, in_pts_upper, alpha,
+ secondary_stream_idx, secondary_pts, fade_alpha);
}
}
}
destination->setFrame(stream_idx, *it, /*interpolated=*/false);
}
+
+void Player::take_queue_spot()
+{
+ unique_lock<mutex> lock(queue_state_mu);
+ ++num_queued_frames;
+}
+
+void Player::release_queue_spot()
+{
+ unique_lock<mutex> lock(queue_state_mu);
+ assert(num_queued_frames > 0);
+ --num_queued_frames;
+ new_clip_changed.notify_all();
+}
#define _PLAYER_H 1
#include "clip_list.h"
+#include "queue_spot_holder.h"
extern "C" {
#include <libavformat/avio.h>
class QSurface;
class QSurfaceFormat;
-class Player {
+class Player : public QueueInterface {
public:
Player(JPEGFrameView *destination, bool also_output_to_stream);
using progress_callback_func = std::function<void(double played_this_clip, double total_length)>;
void set_progress_callback(progress_callback_func cb) { progress_callback = cb; }
+ // QueueInterface.
+ void take_queue_spot() override;
+ void release_queue_spot() override;
+
private:
void thread_func(bool also_output_to_stream);
void open_output_stream();
--- /dev/null
+#ifndef _QUEUE_SPOT_HOLDER
+#define _QUEUE_SPOT_HOLDER 1
+
+// A RAII class to hold a shared resource, in our case an (unordered!) spot in a queue,
+// for as long as a frame is under computation.
+
+class QueueInterface {
+public:
+ virtual ~QueueInterface() {}
+ virtual void take_queue_spot() = 0;
+ virtual void release_queue_spot() = 0;
+};
+
+class QueueSpotHolder {
+public:
+ QueueSpotHolder() : queue(nullptr) {}
+
+ explicit QueueSpotHolder(QueueInterface *queue) : queue(queue) {
+ queue->take_queue_spot();
+ }
+
+ QueueSpotHolder(QueueSpotHolder &&other) : queue(other.queue) {
+ other.queue = nullptr;
+ }
+
+ QueueSpotHolder &operator=(QueueSpotHolder &&other) {
+ queue = other.queue;
+ other.queue = nullptr;
+ return *this;
+ }
+
+ ~QueueSpotHolder() {
+ if (queue != nullptr) {
+ queue->release_queue_spot();
+ }
+ }
+
+ // Movable only.
+ QueueSpotHolder(QueueSpotHolder &) = delete;
+ QueueSpotHolder &operator=(QueueSpotHolder &) = delete;
+
+private:
+ QueueInterface *queue;
+};
+
+#endif // !defined(_QUEUE_SPOT_HOLDER)
void VideoStream::schedule_original_frame(steady_clock::time_point local_pts,
int64_t output_pts, function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder,
unsigned stream_idx, int64_t input_pts)
{
fprintf(stderr, "output_pts=%ld original input_pts=%ld\n", output_pts, input_pts);
qf.stream_idx = stream_idx;
qf.input_first_pts = input_pts;
qf.display_func = move(display_func);
+ qf.queue_spot_holder = move(queue_spot_holder);
unique_lock<mutex> lock(queue_lock);
frame_queue.push_back(move(qf));
queue_changed.notify_all();
}
-bool VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64_t output_pts,
- function<void()> &&display_func, unsigned stream_idx,
- int64_t input_pts, int secondary_stream_idx,
+void VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64_t output_pts,
+ function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder,
+ unsigned stream_idx, int64_t input_pts, int secondary_stream_idx,
int64_t secondary_input_pts, float fade_alpha)
{
fprintf(stderr, "output_pts=%ld faded input_pts=%ld,%ld fade_alpha=%.2f\n", output_pts, input_pts, secondary_input_pts, fade_alpha);
unique_lock<mutex> lock(queue_lock);
if (interpolate_resources.empty()) {
fprintf(stderr, "WARNING: Too many interpolated frames already in transit; dropping one.\n");
- return false;
+ return;
}
resources = interpolate_resources.front();
interpolate_resources.pop_front();
qf.resources = resources;
qf.input_first_pts = input_pts;
qf.display_func = move(display_func);
+ qf.queue_spot_holder = move(queue_spot_holder);
qf.secondary_stream_idx = secondary_stream_idx;
qf.secondary_input_pts = secondary_input_pts;
unique_lock<mutex> lock(queue_lock);
frame_queue.push_back(move(qf));
queue_changed.notify_all();
- return true;
}
-bool VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts,
+void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts,
int64_t output_pts, function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder,
unsigned stream_idx, int64_t input_first_pts,
int64_t input_second_pts, float alpha,
int secondary_stream_idx, int64_t secondary_input_pts,
unique_lock<mutex> lock(queue_lock);
if (interpolate_resources.empty()) {
fprintf(stderr, "WARNING: Too many interpolated frames already in transit; dropping one.\n");
- return false;
+ return;
}
resources = interpolate_resources.front();
interpolate_resources.pop_front();
qf.resources = resources;
qf.id = id;
qf.display_func = move(display_func);
+ qf.queue_spot_holder = move(queue_spot_holder);
check_error();
unique_lock<mutex> lock(queue_lock);
frame_queue.push_back(move(qf));
queue_changed.notify_all();
- return true;
}
void VideoStream::schedule_refresh_frame(steady_clock::time_point local_pts,
- int64_t output_pts, function<void()> &&display_func)
+ int64_t output_pts, function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder)
{
QueuedFrame qf;
qf.type = QueuedFrame::REFRESH;
qf.output_pts = output_pts;
qf.display_func = move(display_func);
+ qf.queue_spot_holder = move(queue_spot_holder);
unique_lock<mutex> lock(queue_lock);
frame_queue.push_back(move(qf));
#include "jpeg_frame_view.h"
#include "ref_counted_gl_sync.h"
+#include "queue_spot_holder.h"
#include <chrono>
#include <condition_variable>
void clear_queue();
// “display_func” is called after the frame has been calculated (if needed)
- // and has gone out to the stream. Returns false on failure (ie., couldn't
- // schedule the frame due to lack of resources).
+ // and has gone out to the stream.
void schedule_original_frame(std::chrono::steady_clock::time_point,
int64_t output_pts, std::function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder,
unsigned stream_idx, int64_t input_pts);
- bool schedule_faded_frame(std::chrono::steady_clock::time_point, int64_t output_pts,
- std::function<void()> &&display_func, unsigned stream_idx,
- int64_t input_pts, int secondary_stream_idx,
+ void schedule_faded_frame(std::chrono::steady_clock::time_point, int64_t output_pts,
+ std::function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder,
+ unsigned stream_idx, int64_t input_pts, int secondary_stream_idx,
int64_t secondary_input_pts, float fade_alpha);
- bool schedule_interpolated_frame(std::chrono::steady_clock::time_point, int64_t output_pts,
- std::function<void()> &&display_func, unsigned stream_idx,
- int64_t input_first_pts, int64_t input_second_pts, float alpha,
- int secondary_stream_idx = -1, int64_t secondary_inputs_pts = -1,
+ void schedule_interpolated_frame(std::chrono::steady_clock::time_point, int64_t output_pts,
+ std::function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder,
+ unsigned stream_idx, int64_t input_first_pts, int64_t input_second_pts,
+ float alpha, int secondary_stream_idx = -1, int64_t secondary_inputs_pts = -1,
float fade_alpha = 0.0f); // -1 = no secondary frame.
void schedule_refresh_frame(std::chrono::steady_clock::time_point, int64_t output_pts,
- std::function<void()> &&display_func);
+ std::function<void()> &&display_func,
+ QueueSpotHolder &&queue_spot_holder);
private:
void encode_thread_func();
JPEGID id;
std::function<void()> display_func; // Called when the image is done decoding.
+
+ QueueSpotHolder queue_spot_holder;
};
std::deque<QueuedFrame> frame_queue; // Under <queue_lock>.
std::mutex queue_lock;