X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fscene%2Fscene_producer.cpp;h=b6339e85af612f96f52ec2967cea141b074a1404;hb=ceffb3558efafb706af50c04b4b150250e2963f0;hp=84161ad1ca173f1135267334b1833077e6a555b8;hpb=3278113954205d82eb6d5b0f10ddae852e72444f;p=casparcg diff --git a/core/producer/scene/scene_producer.cpp b/core/producer/scene/scene_producer.cpp index 84161ad1c..b6339e85a 100644 --- a/core/producer/scene/scene_producer.cpp +++ b/core/producer/scene/scene_producer.cpp @@ -36,7 +36,9 @@ namespace caspar { namespace core { namespace scene { layer::layer(const std::wstring& name, const spl::shared_ptr& producer) - : name(name), producer(producer) + : name(name) + , producer(producer) + , volume(1.0) { crop.lower_right.x.bind(producer.get()->pixel_constraints().width); crop.lower_right.y.bind(producer.get()->pixel_constraints().height); @@ -48,6 +50,18 @@ layer::layer(const std::wstring& name, const spl::shared_ptr& pr adjustments::adjustments() : opacity(1.0) + , contrast(1.0) + , saturation(1.0) + , brightness(1.0) +{ +} + +levels::levels() + : min_input(0.0) + , max_input(1.0) + , gamma(1.0) + , min_output(0.0) + , max_output(1.0) { } @@ -119,40 +133,56 @@ struct marker struct scene_producer::impl { std::wstring producer_name_; + std::wstring template_name_; constraints pixel_constraints_; video_format_desc format_desc_; std::list layers_; interaction_aggregator aggregator_; - binding frame_number_; + binding frame_number_; + binding timeline_frame_number_; binding speed_; mutable tbb::atomic m_x_; mutable tbb::atomic m_y_; binding mouse_x_; binding mouse_y_; - double frame_fraction_ = 0.0; + double frame_fraction_ = 0.0; std::map timelines_; std::map> variables_; std::vector variable_names_; std::multimap markers_by_frame_; + std::vector> task_subscriptions_; monitor::subject monitor_subject_; - bool paused_ = true; - bool removed_ = false; - bool going_to_mark_ = false; - - impl(std::wstring producer_name, int width, int height, const video_format_desc& format_desc) + bool paused_ = true; + bool removed_ = false; + bool going_to_mark_ = false; + + impl( + std::wstring producer_name, + std::wstring template_name, + int width, + int height, + const video_format_desc& format_desc) : producer_name_(std::move(producer_name)) - , pixel_constraints_(width, height) + , template_name_(std::move(template_name)) , format_desc_(format_desc) , aggregator_([=] (double x, double y) { return collission_detect(x, y); }) { auto speed_variable = std::make_shared>(L"1.0", true, 1.0); store_variable(L"scene_speed", speed_variable); speed_ = speed_variable->value(); - - auto frame_variable = std::make_shared>(L"-1", true, -1); + + auto frame_variable = std::make_shared>(L"-1", true, -1); store_variable(L"frame", frame_variable); frame_number_ = frame_variable->value(); + auto fps = format_desc_.fps * format_desc_.field_count; + auto fps_variable = std::make_shared>(boost::lexical_cast(fps), false, fps); + store_variable(L"fps", fps_variable); + + auto timeline_frame_variable = std::make_shared>(L"-1", false, -1); + store_variable(L"timeline_frame", timeline_frame_variable); + timeline_frame_number_ = timeline_frame_variable->value(); + auto mouse_x_variable = std::make_shared>(L"0", false, 0); auto mouse_y_variable = std::make_shared>(L"0", false, 0); store_variable(L"mouse_x", mouse_x_variable); @@ -161,6 +191,13 @@ struct scene_producer::impl mouse_y_ = mouse_y_variable->value(); m_x_ = 0; m_y_ = 0; + + auto scene_width = std::make_shared>(boost::lexical_cast(width), false, width); + auto scene_height = std::make_shared>(boost::lexical_cast(height), false, height); + store_variable(L"scene_width", scene_width); + store_variable(L"scene_height", scene_height); + pixel_constraints_.width = scene_width->value(); + pixel_constraints_.height = scene_height->value(); } layer& create_layer( @@ -170,6 +207,8 @@ struct scene_producer::impl layer.position.x.set(x); layer.position.y.set(y); + layer.clip.lower_right.x.bind(pixel_constraints_.width); + layer.clip.lower_right.y.bind(pixel_constraints_.height); layers_.push_back(layer); @@ -180,6 +219,15 @@ struct scene_producer::impl layers_.reverse(); } + layer& get_layer(const std::wstring& name) + { + for (auto& layer : layers_) + if (layer.name.get() == name) + return layer; + + CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene")); + } + void store_keyframe(void* timeline_identity, const keyframe& k) { timelines_[timeline_identity].keyframes.insert(std::make_pair(k.destination_frame, k)); @@ -197,6 +245,27 @@ struct scene_producer::impl markers_by_frame_.insert(std::make_pair(frame, marker(action, label))); } + void add_task(binding when, std::function task) + { + auto subscription = when.on_change([=] + { + if (when.get()) + { + try + { + task(); + } + catch (...) + { + CASPAR_LOG_CURRENT_EXCEPTION_AT_LEVEL(debug); + CASPAR_LOG(error) << print() << " Error when invoking scene task. Turn on log level debug for stacktrace."; + } + } + }); + + task_subscriptions_.push_back(std::move(subscription)); + } + core::variable& get_variable(const std::wstring& name) { auto found = variables_.find(name); @@ -212,9 +281,9 @@ struct scene_producer::impl return variable_names_; } - binding frame() + binding timeline_frame() { - return frame_number_; + return timeline_frame_number_; } frame_transform get_transform(const layer& layer) const @@ -224,41 +293,68 @@ struct scene_producer::impl auto& anchor = transform.image_transform.anchor; auto& pos = transform.image_transform.fill_translation; auto& scale = transform.image_transform.fill_scale; + auto& clip_pos = transform.image_transform.clip_translation; + auto& clip_scale = transform.image_transform.clip_scale; auto& angle = transform.image_transform.angle; auto& crop = transform.image_transform.crop; auto& pers = transform.image_transform.perspective; - - anchor[0] = layer.anchor.x.get() / layer.producer.get()->pixel_constraints().width.get(); - anchor[1] = layer.anchor.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pos[0] = layer.position.x.get() / pixel_constraints_.width.get(); - pos[1] = layer.position.y.get() / pixel_constraints_.height.get(); - scale[0] = layer.producer.get()->pixel_constraints().width.get() / pixel_constraints_.width.get(); - scale[1] = layer.producer.get()->pixel_constraints().height.get() / pixel_constraints_.height.get(); - crop.ul[0] = layer.crop.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); - crop.ul[1] = layer.crop.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); - crop.lr[0] = layer.crop.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); - crop.lr[1] = layer.crop.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.ul[0] = layer.perspective.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.ul[1] = layer.perspective.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.ur[0] = layer.perspective.upper_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.ur[1] = layer.perspective.upper_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.lr[0] = layer.perspective.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.lr[1] = layer.perspective.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.ll[0] = layer.perspective.lower_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.ll[1] = layer.perspective.lower_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); + auto& levels = transform.image_transform.levels; + + anchor[0] = layer.anchor.x.get() / layer.producer.get()->pixel_constraints().width.get(); + anchor[1] = layer.anchor.y.get() / layer.producer.get()->pixel_constraints().height.get(); + + pos[0] = layer.position.x.get() / pixel_constraints_.width.get(); + pos[1] = layer.position.y.get() / pixel_constraints_.height.get(); + scale[0] = layer.producer.get()->pixel_constraints().width.get() / pixel_constraints_.width.get(); + scale[1] = layer.producer.get()->pixel_constraints().height.get() / pixel_constraints_.height.get(); + + clip_pos[0] = layer.clip.upper_left.x.get() / pixel_constraints_.width.get(); + clip_pos[1] = layer.clip.upper_left.y.get() / pixel_constraints_.height.get(); + clip_scale[0] = layer.clip.lower_right.x.get() / pixel_constraints_.width.get() - clip_pos[0]; + clip_scale[1] = layer.clip.lower_right.y.get() / pixel_constraints_.height.get() - clip_pos[1]; + + crop.ul[0] = layer.crop.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); + crop.ul[1] = layer.crop.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); + crop.lr[0] = layer.crop.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); + crop.lr[1] = layer.crop.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); + + pers.ul[0] = layer.perspective.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.ul[1] = layer.perspective.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); + pers.ur[0] = layer.perspective.upper_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.ur[1] = layer.perspective.upper_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); + pers.lr[0] = layer.perspective.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.lr[1] = layer.perspective.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); + pers.ll[0] = layer.perspective.lower_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.ll[1] = layer.perspective.lower_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); static const double PI = 3.141592653589793; - angle = layer.rotation.get() * PI / 180.0; - - transform.image_transform.opacity = layer.adjustments.opacity.get(); - transform.image_transform.is_key = layer.is_key.get(); - transform.image_transform.use_mipmap = layer.use_mipmap.get(); - transform.image_transform.blend_mode = layer.blend_mode.get(); - transform.image_transform.chroma.key = layer.chroma_key.key.get(); - transform.image_transform.chroma.threshold = layer.chroma_key.threshold.get(); - transform.image_transform.chroma.softness = layer.chroma_key.softness.get(); - transform.image_transform.chroma.spill = layer.chroma_key.spill.get(); + angle = layer.rotation.get() * PI / 180.0; + + levels.min_input = layer.levels.min_input.get(); + levels.max_input = layer.levels.max_input.get(); + levels.gamma = layer.levels.gamma.get(); + levels.min_output = layer.levels.min_output.get(); + levels.max_output = layer.levels.max_output.get(); + + transform.image_transform.opacity = layer.adjustments.opacity.get(); + transform.image_transform.contrast = layer.adjustments.contrast.get(); + transform.image_transform.saturation = layer.adjustments.saturation.get(); + transform.image_transform.brightness = layer.adjustments.brightness.get(); + transform.image_transform.is_key = layer.is_key.get(); + transform.image_transform.use_mipmap = layer.use_mipmap.get(); + transform.image_transform.blend_mode = layer.blend_mode.get(); + + transform.image_transform.chroma.enable = layer.chroma_key.enable.get(); + transform.image_transform.chroma.target_hue = layer.chroma_key.target_hue.get(); + transform.image_transform.chroma.hue_width = layer.chroma_key.hue_width.get(); + transform.image_transform.chroma.min_saturation = layer.chroma_key.min_saturation.get(); + transform.image_transform.chroma.min_brightness = layer.chroma_key.min_brightness.get(); + transform.image_transform.chroma.softness = layer.chroma_key.softness.get(); + transform.image_transform.chroma.spill_suppress = layer.chroma_key.spill_suppress.get(); + transform.image_transform.chroma.spill_suppress_saturation = layer.chroma_key.spill_suppress_saturation.get(); + + transform.audio_transform.volume = layer.volume.get(); // Mark as sublayer, so it will be composited separately by the mixer. transform.image_transform.layer_depth = 1; @@ -311,9 +407,9 @@ struct scene_producer::impl { prec_timer timer; timer.tick_millis(0); - + auto field1 = render_progressive_frame(); - + timer.tick(0.5 / format_desc_.fps); auto field2 = render_progressive_frame(); @@ -329,8 +425,8 @@ struct scene_producer::impl if (std::abs(frame_fraction_) >= 1.0) { int64_t delta = static_cast(frame_fraction_); - auto previous_frame = frame_number_.get(); - auto next_frame = frame_number_.get() + delta; + auto previous_frame = timeline_frame_number_.get(); + auto next_frame = timeline_frame_number_.get() + delta; auto marker = find_first_stop_or_jump_or_remove(previous_frame + 1, next_frame); if (marker && marker->second.action == mark_action::remove) @@ -341,7 +437,7 @@ struct scene_producer::impl { if (marker->second.action == mark_action::stop) { - frame_number_.set(marker->first); + timeline_frame_number_.set(marker->first); frame_fraction_ = 0.0; paused_ = true; } @@ -352,7 +448,7 @@ struct scene_producer::impl } else { - frame_number_.set(next_frame); + timeline_frame_number_.set(next_frame); frame_fraction_ -= delta; } @@ -365,14 +461,16 @@ struct scene_producer::impl if (removed_) return draw_frame::empty(); + mouse_x_.set(m_x_); + mouse_y_.set(m_y_); + if (!paused_) advance(); - for (auto& timeline : timelines_) - timeline.second.on_frame(frame_number_.get()); + frame_number_.set(frame_number_.get() + speed_.get()); - mouse_x_.set(m_x_); - mouse_y_.set(m_y_); + for (auto& timeline : timelines_) + timeline.second.on_frame(timeline_frame_number_.get()); std::vector frames; @@ -425,7 +523,7 @@ struct scene_producer::impl return boost::optional(); } - std::future call(const std::vector& params) + std::future call(const std::vector& params) { if (!params.empty() && boost::ends_with(params.at(0), L"()")) return make_ready_future(handle_call(params)); @@ -470,11 +568,11 @@ struct scene_producer::impl void next() { - auto marker = find_first_start(frame_number_.get() + 1); + auto marker = find_first_start(timeline_frame_number_.get() + 1); if (marker) { - frame_number_.set(marker->first - 1); + timeline_frame_number_.set(marker->first - 1); frame_fraction_ = 0.0; paused_ = false; going_to_mark_ = true; @@ -491,7 +589,7 @@ struct scene_producer::impl { if (marker.second.label_argument == marker_name && marker.second.action == mark_action::start) { - frame_number_.set(marker.first + offset); + timeline_frame_number_.set(marker.first + offset); frame_fraction_ = 0.0; paused_ = false; going_to_mark_ = true; @@ -502,7 +600,7 @@ struct scene_producer::impl if (marker_name == L"intro") { - frame_number_.set(offset); + timeline_frame_number_.set(offset); frame_fraction_ = 0.0; paused_ = false; going_to_mark_ = true; @@ -517,20 +615,22 @@ struct scene_producer::impl std::wstring print() const { - return L"scene[type=" + name() + L"]"; + return L"scene[type=" + name() + L" template=" + template_name_ + L"]"; } std::wstring name() const { return producer_name_; } - + boost::property_tree::wptree info() const { boost::property_tree::wptree info; info.add(L"type", L"scene"); info.add(L"producer-name", name()); + info.add(L"template-name", template_name_); info.add(L"frame-number", frame_number_.get()); + info.add(L"timeline-frame-number", timeline_frame_number_.get()); for (auto& var : variables_) { @@ -566,8 +666,8 @@ struct scene_producer::impl } }; -scene_producer::scene_producer(std::wstring producer_name, int width, int height, const video_format_desc& format_desc) - : impl_(new impl(std::move(producer_name), width, height, format_desc)) +scene_producer::scene_producer(std::wstring producer_name, std::wstring template_name, int width, int height, const video_format_desc& format_desc) + : impl_(new impl(std::move(producer_name), std::move(template_name), width, height, format_desc)) { } @@ -587,13 +687,19 @@ layer& scene_producer::create_layer( return impl_->create_layer(producer, 0, 0, name); } -void scene_producer::reverse_layers() { +void scene_producer::reverse_layers() +{ impl_->reverse_layers(); } -binding scene_producer::frame() +layer& scene_producer::get_layer(const std::wstring& name) +{ + return impl_->get_layer(name); +} + +binding scene_producer::timeline_frame() { - return impl_->frame(); + return impl_->timeline_frame(); } draw_frame scene_producer::receive_impl() @@ -628,7 +734,7 @@ boost::property_tree::wptree scene_producer::info() const return impl_->info(); } -std::future scene_producer::call(const std::vector& params) +std::future scene_producer::call(const std::vector& params) { return impl_->call(params); } @@ -654,6 +760,11 @@ void scene_producer::add_mark(int64_t frame, mark_action action, const std::wstr impl_->add_mark(frame, action, label); } +void scene_producer::add_task(binding when, std::function task) +{ + impl_->add_task(std::move(when), std::move(task)); +} + core::variable& scene_producer::get_variable(const std::wstring& name) { return impl_->get_variable(name);