Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 103 additions & 5 deletions src/core/producer/layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
#include "../frame/draw_frame.h"
#include "../video_format.h"

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <common/future.h>
#include <optional>

namespace caspar { namespace core {

struct layer::impl
Expand All @@ -38,8 +43,10 @@ struct layer::impl
spl::shared_ptr<frame_producer> foreground_ = frame_producer::empty();
spl::shared_ptr<frame_producer> background_ = frame_producer::empty();

bool auto_play_ = false;
bool paused_ = false;
bool auto_play_ = false;
bool paused_ = false;
std::optional<uint32_t> foreground_auto_length_override_;
std::optional<uint32_t> background_auto_length_override_;

public:
impl(const core::video_format_desc format_desc)
Expand All @@ -55,6 +62,23 @@ struct layer::impl
{
background_ = std::move(producer);
auto_play_ = auto_play;
background_auto_length_override_.reset(); // Clear any previous auto_length override

if (auto_play_ && foreground_ == frame_producer::empty()) {
play();
} else if (preview_producer) {
preview(true);
}
}

void load(spl::shared_ptr<frame_producer> producer,
bool preview_producer,
bool auto_play,
std::optional<uint32_t> auto_length)
{
background_ = std::move(producer);
auto_play_ = auto_play;
background_auto_length_override_ = auto_length;

if (auto_play_ && foreground_ == frame_producer::empty()) {
play();
Expand Down Expand Up @@ -88,8 +112,10 @@ struct layer::impl
}
}

foreground_ = std::move(background_);
background_ = frame_producer::empty();
foreground_ = std::move(background_);
background_ = frame_producer::empty();
foreground_auto_length_override_ = background_auto_length_override_;
background_auto_length_override_.reset();

auto_play_ = false;
}
Expand All @@ -101,6 +127,8 @@ struct layer::impl
{
foreground_ = frame_producer::empty();
auto_play_ = false;
foreground_auto_length_override_.reset();
background_auto_length_override_.reset();
}

draw_frame receive(const video_field field, int nb_samples)
Expand All @@ -115,7 +143,9 @@ struct layer::impl
auto auto_play_delta = background_->auto_play_delta();
if (auto_play_delta) {
auto time = static_cast<std::int64_t>(foreground_->frame_number());
auto duration = static_cast<std::int64_t>(foreground_->nb_frames());
auto duration = foreground_auto_length_override_
? static_cast<std::int64_t>(*foreground_auto_length_override_)
: static_cast<std::int64_t>(foreground_->nb_frames());
frames_left = duration - time - *auto_play_delta;
if (frames_left < 1 && field != video_field::b) {
play();
Expand Down Expand Up @@ -159,6 +189,66 @@ struct layer::impl
return draw_frame{};
}
}

std::future<std::wstring> call(const std::vector<std::wstring>& params)
{
if (params.empty()) {
return make_ready_future(std::wstring(L""));
}

std::wstring cmd = boost::to_upper_copy(params[0]);

// Handle layer-specific commands
if (cmd == L"FRAMES_LEFT") {
if (params.size() > 1) {
std::wstring value = boost::to_upper_copy(params[1]);
if (value == L"CLEAR" || value == L"RESET") {
foreground_auto_length_override_.reset();
auto_play_ = false; // Disable AUTO entirely
return make_ready_future(std::wstring(L"FRAMES_LEFT CLEARED AND AUTO PLAY DISABLED"));
} else {
// Set new countdown
try {
uint32_t frames_left_value = boost::lexical_cast<uint32_t>(params[1]);
uint32_t current_frame = foreground_->frame_number();
foreground_auto_length_override_ = current_frame + frames_left_value;

// Smart AUTO enabling: if there's a background but AUTO isn't set, enable it
if (!auto_play_ && background_ != frame_producer::empty()) {
auto_play_ = true;
return make_ready_future(std::wstring(L"FRAMES_LEFT SET TO " +
boost::lexical_cast<std::wstring>(frames_left_value) +
L" AND AUTO PLAY ENABLED"));
} else {
return make_ready_future(std::wstring(
L"FRAMES_LEFT SET TO " + boost::lexical_cast<std::wstring>(frames_left_value)));
}
} catch (const boost::bad_lexical_cast&) {
return make_ready_future(std::wstring(L"ERROR: Invalid frames value"));
}
}
} else {
// Query current frames left
if (auto_play_ && foreground_auto_length_override_) {
uint32_t current_frame = foreground_->frame_number();
int32_t frames_remaining =
static_cast<int32_t>(*foreground_auto_length_override_) - static_cast<int32_t>(current_frame);

if (background_->auto_play_delta()) {
frames_remaining -= static_cast<int32_t>(*background_->auto_play_delta());
}

return make_ready_future(std::wstring(
L"FRAMES_LEFT " + boost::lexical_cast<std::wstring>(std::max(0, frames_remaining))));
} else {
return make_ready_future(std::wstring(L"AUTO PLAY NOT ACTIVE"));
}
}
}

// Forward all other commands to the foreground producer
return foreground_->call(params);
}
};

layer::layer(const core::video_format_desc format_desc)
Expand All @@ -179,6 +269,13 @@ void layer::load(spl::shared_ptr<frame_producer> frame_producer, bool preview, b
{
return impl_->load(std::move(frame_producer), preview, auto_play);
}
void layer::load(spl::shared_ptr<frame_producer> frame_producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length)
{
return impl_->load(std::move(frame_producer), preview, auto_play, auto_length);
}
void layer::play() { impl_->play(); }
void layer::preview() { impl_->preview(false); }
void layer::pause() { impl_->pause(); }
Expand All @@ -192,5 +289,6 @@ draw_frame layer::receive_background(const video_field field, int nb_samples)
spl::shared_ptr<frame_producer> layer::foreground() const { return impl_->foreground_; }
spl::shared_ptr<frame_producer> layer::background() const { return impl_->background_; }
bool layer::has_background() const { return impl_->background_ != frame_producer::empty(); }
std::future<std::wstring> layer::call(const std::vector<std::wstring>& params) { return impl_->call(params); }
core::monitor::state layer::state() const { return impl_->state_; }
}} // namespace caspar::core
4 changes: 4 additions & 0 deletions src/core/producer/layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class layer final
void swap(layer& other);

void load(spl::shared_ptr<frame_producer> producer, bool preview, bool auto_play = false);
void
load(spl::shared_ptr<frame_producer> producer, bool preview, bool auto_play, std::optional<uint32_t> auto_length);
void play();
void preview();
void pause();
Expand All @@ -53,6 +55,8 @@ class layer final
draw_frame receive(const video_field field, int nb_samples);
draw_frame receive_background(const video_field field, int nb_samples);

std::future<std::wstring> call(const std::vector<std::wstring>& params);

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

spl::shared_ptr<frame_producer> foreground() const;
Expand Down
28 changes: 27 additions & 1 deletion src/core/producer/stage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ struct stage::impl : public std::enable_shared_from_this<impl>
return executor_.begin_invoke([=] { get_layer(index).load(producer, preview, auto_play); });
}

std::future<void> load(int index,
const spl::shared_ptr<frame_producer>& producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length)
{
return executor_.begin_invoke([=] { get_layer(index).load(producer, preview, auto_play, auto_length); });
}

std::future<void> preview(int index)
{
return executor_.begin_invoke([=] { get_layer(index).preview(); });
Expand Down Expand Up @@ -399,7 +408,7 @@ struct stage::impl : public std::enable_shared_from_this<impl>

std::future<std::wstring> call(int index, const std::vector<std::wstring>& params)
{
return flatten(executor_.begin_invoke([=] { return get_layer(index).foreground()->call(params).share(); }));
return flatten(executor_.begin_invoke([=] { return get_layer(index).call(params).share(); }));
}
std::future<std::wstring> callbg(int index, const std::vector<std::wstring>& params)
{
Expand Down Expand Up @@ -457,6 +466,14 @@ std::future<void> stage::load(int index, const spl::shared_ptr<frame_producer>&
{
return impl_->load(index, producer, preview, auto_play);
}
std::future<void> stage::load(int index,
const spl::shared_ptr<frame_producer>& producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length)
{
return impl_->load(index, producer, preview, auto_play, auto_length);
}
std::future<void> stage::preview(int index) { return impl_->preview(index); }
std::future<void> stage::pause(int index) { return impl_->pause(index); }
std::future<void> stage::resume(int index) { return impl_->resume(index); }
Expand Down Expand Up @@ -547,6 +564,15 @@ stage_delayed::load(int index, const spl::shared_ptr<frame_producer>& producer,
{
return executor_.begin_invoke([=]() { return stage_->load(index, producer, preview, auto_play).get(); });
}
std::future<void> stage_delayed::load(int index,
const spl::shared_ptr<frame_producer>& producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length)
{
return executor_.begin_invoke(
[=]() { return stage_->load(index, producer, preview, auto_play, auto_length).get(); });
}
std::future<void> stage_delayed::preview(int index)
{
return executor_.begin_invoke([=]() { return stage_->preview(index).get(); });
Expand Down
16 changes: 16 additions & 0 deletions src/core/producer/stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <future>
#include <map>
#include <mutex>
#include <optional>
#include <tuple>
#include <vector>

Expand Down Expand Up @@ -82,6 +83,11 @@ class stage_base
virtual std::future<frame_transform> get_current_transform(int index) = 0;
virtual std::future<void>
load(int index, const spl::shared_ptr<frame_producer>& producer, bool preview = false, bool auto_play = false) = 0;
virtual std::future<void> load(int index,
const spl::shared_ptr<frame_producer>& producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length) = 0;
virtual std::future<void> preview(int index) = 0;
virtual std::future<void> pause(int index) = 0;
virtual std::future<void> resume(int index) = 0;
Expand Down Expand Up @@ -132,6 +138,11 @@ class stage final : public stage_base
const spl::shared_ptr<frame_producer>& producer,
bool preview = false,
bool auto_play = false) override;
std::future<void> load(int index,
const spl::shared_ptr<frame_producer>& producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length) override;
std::future<void> preview(int index) override;
std::future<void> pause(int index) override;
std::future<void> resume(int index) override;
Expand Down Expand Up @@ -188,6 +199,11 @@ class stage_delayed final : public stage_base
const spl::shared_ptr<frame_producer>& producer,
bool preview = false,
bool auto_play = false) override;
std::future<void> load(int index,
const spl::shared_ptr<frame_producer>& producer,
bool preview,
bool auto_play,
std::optional<uint32_t> auto_length) override;
std::future<void> preview(int index) override;
std::future<void> pause(int index) override;
std::future<void> resume(int index) override;
Expand Down
24 changes: 20 additions & 4 deletions src/protocol/amcp/AMCPCommandsImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,16 @@ std::wstring loadbg_command(command_context& ctx)
transition_producer = create_transition_producer(new_producer, transitionInfo);
}

// TODO - we should pass the format into load(), so that we can catch it having changed since the producer was
// initialised
ctx.channel.stage->load(ctx.layer_index(), transition_producer, false, auto_play); // TODO: LOOP
std::optional<uint32_t> auto_length_param;
if (contains_param(L"AUTO_LENGTH", ctx.parameters)) {
auto_length_param = get_param(L"AUTO_LENGTH", ctx.parameters, static_cast<uint32_t>(0));
}

if (auto_length_param) {
ctx.channel.stage->load(ctx.layer_index(), transition_producer, false, auto_play, auto_length_param);
} else {
ctx.channel.stage->load(ctx.layer_index(), transition_producer, false, auto_play);
}
} catch (file_not_found&) {
if (contains_param(L"CLEAR_ON_404", ctx.parameters)) {
ctx.channel.stage->load(
Expand All @@ -337,7 +344,16 @@ std::wstring load_command(command_context& ctx)
get_producer_dependencies(ctx.channel.raw_channel, ctx), ctx.parameters);
auto transition_producer = create_transition_producer(new_producer, transition_info{});

ctx.channel.stage->load(ctx.layer_index(), transition_producer, true);
std::optional<uint32_t> auto_length_param;
if (contains_param(L"AUTO_LENGTH", ctx.parameters)) {
auto_length_param = get_param(L"AUTO_LENGTH", ctx.parameters, static_cast<uint32_t>(0));
}

if (auto_length_param) {
ctx.channel.stage->load(ctx.layer_index(), transition_producer, true, false, auto_length_param);
} else {
ctx.channel.stage->load(ctx.layer_index(), transition_producer, true);
}
} catch (file_not_found&) {
if (contains_param(L"CLEAR_ON_404", ctx.parameters)) {
ctx.channel.stage->load(
Expand Down