Skip to content

Commit

Permalink
feat: only mix frames in channels which have a consumer attached
Browse files Browse the repository at this point in the history
If there are no consumers, the mixed frames go nowhere.
This does not impact routes, as they take the frames before mixing has occurred, and the producers will still push their frames to the gpu as before.

This reduces the cost of using channels as a pre-comp
  • Loading branch information
Julusian committed Jan 12, 2025
1 parent 267674c commit d11e0f5
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 11 deletions.
33 changes: 24 additions & 9 deletions src/core/consumer/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ struct output::impl

bool remove(const spl::shared_ptr<frame_consumer>& consumer) { return remove(consumer->index()); }

size_t consumer_count()
{
std::lock_guard<std::mutex> lock(consumers_mutex_);
return consumers_.size();
}

void operator()(const const_frame& input_frame1,
const const_frame& input_frame2,
const core::video_format_desc& format_desc)
{
if (!input_frame1) {
return;
}

auto time = std::move(time_);

if (format_desc_ != format_desc) {
Expand All @@ -107,6 +109,18 @@ struct output::impl
return;
}

// If no frame is provided, this should only happen when the channel has no consumers.
// Take a shortcut and perform the sleep to let the channel tick correctly.
if (!input_frame1) {
if (!time) {
time = std::chrono::high_resolution_clock::now();
} else {
std::this_thread::sleep_until(*time);
}
time_ = *time + std::chrono::microseconds(static_cast<int>(1e6 / format_desc_.hz));
return;
}

const auto bytesPerComponent1 =
input_frame1.pixel_format_desc().planes.at(0).depth == common::bit_depth::bit8 ? 1 : 2;
if (input_frame1.size() != format_desc_.size * bytesPerComponent1) {
Expand Down Expand Up @@ -204,11 +218,12 @@ output::output(const spl::shared_ptr<diagnostics::graph>& graph,
{
}
output::~output() {}
void output::add(int index, const spl::shared_ptr<frame_consumer>& consumer) { impl_->add(index, consumer); }
void output::add(const spl::shared_ptr<frame_consumer>& consumer) { impl_->add(consumer); }
bool output::remove(int index) { return impl_->remove(index); }
bool output::remove(const spl::shared_ptr<frame_consumer>& consumer) { return impl_->remove(consumer); }
void output::operator()(const const_frame& frame, const const_frame& frame2, const video_format_desc& format_desc)
void output::add(int index, const spl::shared_ptr<frame_consumer>& consumer) { impl_->add(index, consumer); }
void output::add(const spl::shared_ptr<frame_consumer>& consumer) { impl_->add(consumer); }
bool output::remove(int index) { return impl_->remove(index); }
bool output::remove(const spl::shared_ptr<frame_consumer>& consumer) { return impl_->remove(consumer); }
size_t output::consumer_count() const { return impl_->consumer_count(); }
void output::operator()(const const_frame& frame, const const_frame& frame2, const video_format_desc& format_desc)
{
return (*impl_)(frame, frame2, format_desc);
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/consumer/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class output final
bool remove(const spl::shared_ptr<frame_consumer>& consumer);
bool remove(int index);

size_t consumer_count() const;

core::monitor::state state() const;

private:
Expand Down
9 changes: 7 additions & 2 deletions src/core/video_channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,16 @@ struct video_channel::impl final
auto stage_frames = (*stage_)(frame_counter_, background_routes, routesCb);
graph_->set_value("produce-time", produce_timer.elapsed() * format_desc.hz * 0.5);

// This is a little race prone, but at worst a new consumer will start with a frame of black
bool has_consumers = output_.consumer_count() > 0;

// Mix
caspar::timer mix_timer;
auto mixed_frame = mixer_(stage_frames.frames, stage_frames.format_desc, stage_frames.nb_samples);
auto mixed_frame =
has_consumers ? mixer_(stage_frames.frames, stage_frames.format_desc, stage_frames.nb_samples)
: const_frame{};
auto mixed_frame2 =
stage_frames.format_desc.field_count == 2
has_consumers && stage_frames.format_desc.field_count == 2
? mixer_(stage_frames.frames2, stage_frames.format_desc, stage_frames.nb_samples)
: const_frame{};
graph_->set_value("mix-time", mix_timer.elapsed() * format_desc.hz * 0.5);
Expand Down

0 comments on commit d11e0f5

Please sign in to comment.