]> git.sesse.net Git - casparcg/blob - core/producer/scene/scene_producer.cpp
b6339e85af612f96f52ec2967cea141b074a1404
[casparcg] / core / producer / scene / scene_producer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #include "../../StdAfx.h"
23
24 #include <common/future.h>
25 #include <common/prec_timer.h>
26
27 #include <boost/algorithm/string/split.hpp>
28 #include <boost/algorithm/string.hpp>
29
30 #include "scene_producer.h"
31
32 #include "../../frame/draw_frame.h"
33 #include "../../interaction/interaction_aggregator.h"
34 #include "../text/text_producer.h"
35
36 namespace caspar { namespace core { namespace scene {
37
38 layer::layer(const std::wstring& name, const spl::shared_ptr<frame_producer>& producer)
39         : name(name)
40         , producer(producer)
41         , volume(1.0)
42 {
43         crop.lower_right.x.bind(producer.get()->pixel_constraints().width);
44         crop.lower_right.y.bind(producer.get()->pixel_constraints().height);
45         perspective.upper_right.x.bind(producer.get()->pixel_constraints().width);
46         perspective.lower_right.x.bind(producer.get()->pixel_constraints().width);
47         perspective.lower_right.y.bind(producer.get()->pixel_constraints().height);
48         perspective.lower_left.y.bind(producer.get()->pixel_constraints().height);
49 }
50
51 adjustments::adjustments()
52         : opacity(1.0)
53         , contrast(1.0)
54         , saturation(1.0)
55         , brightness(1.0)
56 {
57 }
58
59 levels::levels()
60         : min_input(0.0)
61         , max_input(1.0)
62         , gamma(1.0)
63         , min_output(0.0)
64         , max_output(1.0)
65 {
66 }
67
68 struct timeline
69 {
70         std::map<int64_t, keyframe> keyframes;
71
72         void on_frame(int64_t frame)
73         {
74                 auto before = --keyframes.upper_bound(frame);
75                 bool found_before = before != keyframes.end() && before->first < frame;
76                 auto after = keyframes.upper_bound(frame);
77                 bool found_after = after != keyframes.end() && after->first > frame;
78                 auto exact_frame = keyframes.find(frame);
79                 bool found_exact_frame = exact_frame != keyframes.end();
80
81                 if (found_exact_frame)
82                 {
83                         exact_frame->second.on_destination_frame();
84
85                         auto next_frame = ++exact_frame;
86
87                         if (next_frame != keyframes.end() && next_frame->second.on_start_animate)
88                                 next_frame->second.on_start_animate();
89                 }
90                 else if (found_after)
91                 {
92                         int64_t start_frame = 0;
93
94                         if (found_before)
95                         {
96                                 start_frame = before->first;
97                         }
98
99                         if (after->second.on_start_animate && frame == 0)
100                                 after->second.on_start_animate();
101                         else if (after->second.on_animate_to)
102                                 after->second.on_animate_to(start_frame, frame);
103                 }
104         }
105 };
106
107 mark_action get_mark_action(const std::wstring& name)
108 {
109         if (name == L"start")
110                 return mark_action::start;
111         else if (name == L"stop")
112                 return mark_action::stop;
113         else if (name == L"jump_to")
114                 return mark_action::jump_to;
115         else if (name == L"remove")
116                 return mark_action::remove;
117         else
118                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Invalid mark_action " + name));
119 }
120
121 struct marker
122 {
123         mark_action             action;
124         std::wstring    label_argument;
125
126         marker(mark_action action, const std::wstring& label_argument)
127                 : action(action)
128                 , label_argument(label_argument)
129         {
130         }
131 };
132
133 struct scene_producer::impl
134 {
135         std::wstring                                                                                    producer_name_;
136         std::wstring                                                                                    template_name_;
137         constraints                                                                                             pixel_constraints_;
138         video_format_desc                                                                               format_desc_;
139         std::list<layer>                                                                                layers_;
140         interaction_aggregator                                                                  aggregator_;
141         binding<double>                                                                                 frame_number_;
142         binding<int64_t>                                                                                timeline_frame_number_;
143         binding<double>                                                                                 speed_;
144         mutable tbb::atomic<int64_t>                                                    m_x_;
145         mutable tbb::atomic<int64_t>                                                    m_y_;
146         binding<int64_t>                                                                                mouse_x_;
147         binding<int64_t>                                                                                mouse_y_;
148         double                                                                                                  frame_fraction_                 = 0.0;
149         std::map<void*, timeline>                                                               timelines_;
150         std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
151         std::vector<std::wstring>                                                               variable_names_;
152         std::multimap<int64_t, marker>                                                  markers_by_frame_;
153         std::vector<std::shared_ptr<void>>                                              task_subscriptions_;
154         monitor::subject                                                                                monitor_subject_;
155         bool                                                                                                    paused_                                 = true;
156         bool                                                                                                    removed_                                = false;
157         bool                                                                                                    going_to_mark_                  = false;
158
159         impl(
160                         std::wstring producer_name,
161                         std::wstring template_name,
162                         int width,
163                         int height,
164                         const video_format_desc& format_desc)
165                 : producer_name_(std::move(producer_name))
166                 , template_name_(std::move(template_name))
167                 , format_desc_(format_desc)
168                 , aggregator_([=] (double x, double y) { return collission_detect(x, y); })
169         {
170                 auto speed_variable = std::make_shared<core::variable_impl<double>>(L"1.0", true, 1.0);
171                 store_variable(L"scene_speed", speed_variable);
172                 speed_ = speed_variable->value();
173
174                 auto frame_variable = std::make_shared<core::variable_impl<double>>(L"-1", true, -1);
175                 store_variable(L"frame", frame_variable);
176                 frame_number_ = frame_variable->value();
177
178                 auto fps = format_desc_.fps * format_desc_.field_count;
179                 auto fps_variable = std::make_shared<core::variable_impl<double>>(boost::lexical_cast<std::wstring>(fps), false, fps);
180                 store_variable(L"fps", fps_variable);
181
182                 auto timeline_frame_variable = std::make_shared<core::variable_impl<int64_t>>(L"-1", false, -1);
183                 store_variable(L"timeline_frame", timeline_frame_variable);
184                 timeline_frame_number_ = timeline_frame_variable->value();
185
186                 auto mouse_x_variable = std::make_shared<core::variable_impl<int64_t>>(L"0", false, 0);
187                 auto mouse_y_variable = std::make_shared<core::variable_impl<int64_t>>(L"0", false, 0);
188                 store_variable(L"mouse_x", mouse_x_variable);
189                 store_variable(L"mouse_y", mouse_y_variable);
190                 mouse_x_ = mouse_x_variable->value();
191                 mouse_y_ = mouse_y_variable->value();
192                 m_x_ = 0;
193                 m_y_ = 0;
194
195                 auto scene_width = std::make_shared<core::variable_impl<double>>(boost::lexical_cast<std::wstring>(width), false, width);
196                 auto scene_height = std::make_shared<core::variable_impl<double>>(boost::lexical_cast<std::wstring>(height), false, height);
197                 store_variable(L"scene_width", scene_width);
198                 store_variable(L"scene_height", scene_height);
199                 pixel_constraints_.width = scene_width->value();
200                 pixel_constraints_.height = scene_height->value();
201         }
202
203         layer& create_layer(
204                         const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name)
205         {
206                 layer layer(name, producer);
207
208                 layer.position.x.set(x);
209                 layer.position.y.set(y);
210                 layer.clip.lower_right.x.bind(pixel_constraints_.width);
211                 layer.clip.lower_right.y.bind(pixel_constraints_.height);
212
213                 layers_.push_back(layer);
214
215                 return layers_.back();
216         }
217
218         void reverse_layers() {
219                 layers_.reverse();
220         }
221
222         layer& get_layer(const std::wstring& name)
223         {
224                 for (auto& layer : layers_)
225                         if (layer.name.get() == name)
226                                 return layer;
227
228                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene"));
229         }
230
231         void store_keyframe(void* timeline_identity, const keyframe& k)
232         {
233                 timelines_[timeline_identity].keyframes.insert(std::make_pair(k.destination_frame, k));
234         }
235
236         void store_variable(
237                         const std::wstring& name, const std::shared_ptr<core::variable>& var)
238         {
239                 variables_.insert(std::make_pair(name, var));
240                 variable_names_.push_back(name);
241         }
242
243         void add_mark(int64_t frame, mark_action action, const std::wstring& label)
244         {
245                 markers_by_frame_.insert(std::make_pair(frame, marker(action, label)));
246         }
247
248         void add_task(binding<bool> when, std::function<void ()> task)
249         {
250                 auto subscription = when.on_change([=]
251                 {
252                         if (when.get())
253                         {
254                                 try
255                                 {
256                                         task();
257                                 }
258                                 catch (...)
259                                 {
260                                         CASPAR_LOG_CURRENT_EXCEPTION_AT_LEVEL(debug);
261                                         CASPAR_LOG(error) << print() << " Error when invoking scene task. Turn on log level debug for stacktrace.";
262                                 }
263                         }
264                 });
265
266                 task_subscriptions_.push_back(std::move(subscription));
267         }
268
269         core::variable& get_variable(const std::wstring& name)
270         {
271                 auto found = variables_.find(name);
272
273                 if (found == variables_.end())
274                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(name + L" not found in scene"));
275
276                 return *found->second;
277         }
278
279         const std::vector<std::wstring>& get_variables() const
280         {
281                 return variable_names_;
282         }
283
284         binding<int64_t> timeline_frame()
285         {
286                 return timeline_frame_number_;
287         }
288
289         frame_transform get_transform(const layer& layer) const
290         {
291                 frame_transform transform;
292
293                 auto& anchor            = transform.image_transform.anchor;
294                 auto& pos                       = transform.image_transform.fill_translation;
295                 auto& scale                     = transform.image_transform.fill_scale;
296                 auto& clip_pos          = transform.image_transform.clip_translation;
297                 auto& clip_scale        = transform.image_transform.clip_scale;
298                 auto& angle                     = transform.image_transform.angle;
299                 auto& crop                      = transform.image_transform.crop;
300                 auto& pers                      = transform.image_transform.perspective;
301                 auto& levels            = transform.image_transform.levels;
302
303                 anchor[0]               = layer.anchor.x.get()                                                                          / layer.producer.get()->pixel_constraints().width.get();
304                 anchor[1]               = layer.anchor.y.get()                                                                          / layer.producer.get()->pixel_constraints().height.get();
305
306                 pos[0]                  = layer.position.x.get()                                                                        / pixel_constraints_.width.get();
307                 pos[1]                  = layer.position.y.get()                                                                        / pixel_constraints_.height.get();
308                 scale[0]                = layer.producer.get()->pixel_constraints().width.get()         / pixel_constraints_.width.get();
309                 scale[1]                = layer.producer.get()->pixel_constraints().height.get()                / pixel_constraints_.height.get();
310
311                 clip_pos[0]             = layer.clip.upper_left.x.get()                                                         / pixel_constraints_.width.get();
312                 clip_pos[1]             = layer.clip.upper_left.y.get()                                                         / pixel_constraints_.height.get();
313                 clip_scale[0]   = layer.clip.lower_right.x.get()                                                        / pixel_constraints_.width.get() - clip_pos[0];
314                 clip_scale[1]   = layer.clip.lower_right.y.get()                                                        / pixel_constraints_.height.get() - clip_pos[1];
315
316                 crop.ul[0]              = layer.crop.upper_left.x.get()                                                         / layer.producer.get()->pixel_constraints().width.get();
317                 crop.ul[1]              = layer.crop.upper_left.y.get()                                                         / layer.producer.get()->pixel_constraints().height.get();
318                 crop.lr[0]              = layer.crop.lower_right.x.get()                                                        / layer.producer.get()->pixel_constraints().width.get();
319                 crop.lr[1]              = layer.crop.lower_right.y.get()                                                        / layer.producer.get()->pixel_constraints().height.get();
320
321                 pers.ul[0]              = layer.perspective.upper_left.x.get()                                          / layer.producer.get()->pixel_constraints().width.get();
322                 pers.ul[1]              = layer.perspective.upper_left.y.get()                                          / layer.producer.get()->pixel_constraints().height.get();
323                 pers.ur[0]              = layer.perspective.upper_right.x.get()                                         / layer.producer.get()->pixel_constraints().width.get();
324                 pers.ur[1]              = layer.perspective.upper_right.y.get()                                         / layer.producer.get()->pixel_constraints().height.get();
325                 pers.lr[0]              = layer.perspective.lower_right.x.get()                                         / layer.producer.get()->pixel_constraints().width.get();
326                 pers.lr[1]              = layer.perspective.lower_right.y.get()                                         / layer.producer.get()->pixel_constraints().height.get();
327                 pers.ll[0]              = layer.perspective.lower_left.x.get()                                          / layer.producer.get()->pixel_constraints().width.get();
328                 pers.ll[1]              = layer.perspective.lower_left.y.get()                                          / layer.producer.get()->pixel_constraints().height.get();
329
330                 static const double PI = 3.141592653589793;
331
332                 angle = layer.rotation.get() * PI / 180.0;
333
334                 levels.min_input        = layer.levels.min_input.get();
335                 levels.max_input        = layer.levels.max_input.get();
336                 levels.gamma            = layer.levels.gamma.get();
337                 levels.min_output       = layer.levels.min_output.get();
338                 levels.max_output       = layer.levels.max_output.get();
339
340                 transform.image_transform.opacity                                                       = layer.adjustments.opacity.get();
341                 transform.image_transform.contrast                                                      = layer.adjustments.contrast.get();
342                 transform.image_transform.saturation                                            = layer.adjustments.saturation.get();
343                 transform.image_transform.brightness                                            = layer.adjustments.brightness.get();
344                 transform.image_transform.is_key                                                        = layer.is_key.get();
345                 transform.image_transform.use_mipmap                                            = layer.use_mipmap.get();
346                 transform.image_transform.blend_mode                                            = layer.blend_mode.get();
347
348                 transform.image_transform.chroma.enable                                         = layer.chroma_key.enable.get();
349                 transform.image_transform.chroma.target_hue                                     = layer.chroma_key.target_hue.get();
350                 transform.image_transform.chroma.hue_width                                      = layer.chroma_key.hue_width.get();
351                 transform.image_transform.chroma.min_saturation                         = layer.chroma_key.min_saturation.get();
352                 transform.image_transform.chroma.min_brightness                         = layer.chroma_key.min_brightness.get();
353                 transform.image_transform.chroma.softness                                       = layer.chroma_key.softness.get();
354                 transform.image_transform.chroma.spill_suppress                         = layer.chroma_key.spill_suppress.get();
355                 transform.image_transform.chroma.spill_suppress_saturation      = layer.chroma_key.spill_suppress_saturation.get();
356
357                 transform.audio_transform.volume                                                        = layer.volume.get();
358
359                 // Mark as sublayer, so it will be composited separately by the mixer.
360                 transform.image_transform.layer_depth = 1;
361
362                 return transform;
363         }
364
365         boost::optional<std::pair<int64_t, marker>> find_first_stop_or_jump_or_remove(int64_t start_frame, int64_t end_frame)
366         {
367                 auto lower = markers_by_frame_.lower_bound(start_frame);
368                 auto upper = markers_by_frame_.upper_bound(end_frame);
369
370                 if (lower == markers_by_frame_.end())
371                         return boost::none;
372
373                 for (auto iter = lower; iter != upper; ++iter)
374                 {
375                         auto action = iter->second.action;
376
377                         if (action == mark_action::stop || action == mark_action::jump_to || action == mark_action::remove)
378                                 return std::make_pair(iter->first, iter->second);
379                 }
380
381                 return boost::none;
382         }
383
384         boost::optional<std::pair<int64_t, marker>> find_first_start(int64_t start_frame)
385         {
386                 auto lower = markers_by_frame_.lower_bound(start_frame);
387
388                 if (lower == markers_by_frame_.end())
389                         return boost::none;
390
391                 for (auto iter = lower; iter != markers_by_frame_.end(); ++iter)
392                 {
393                         auto action = iter->second.action;
394
395                         if (action == mark_action::start)
396                                 return std::make_pair(iter->first, iter->second);
397                 }
398
399                 return boost::none;
400         }
401
402         draw_frame render_frame()
403         {
404                 if (format_desc_.field_count == 1)
405                         return render_progressive_frame();
406                 else
407                 {
408                         prec_timer timer;
409                         timer.tick_millis(0);
410
411                         auto field1 = render_progressive_frame();
412
413                         timer.tick(0.5 / format_desc_.fps);
414
415                         auto field2 = render_progressive_frame();
416
417                         return draw_frame::interlace(field1, field2, format_desc_.field_mode);
418                 }
419         }
420
421         void advance()
422         {
423                 frame_fraction_ += speed_.get();
424
425                 if (std::abs(frame_fraction_) >= 1.0)
426                 {
427                         int64_t delta = static_cast<int64_t>(frame_fraction_);
428                         auto previous_frame = timeline_frame_number_.get();
429                         auto next_frame = timeline_frame_number_.get() + delta;
430                         auto marker = find_first_stop_or_jump_or_remove(previous_frame + 1, next_frame);
431
432                         if (marker && marker->second.action == mark_action::remove)
433                         {
434                                 remove();
435                         }
436                         if (marker && !going_to_mark_)
437                         {
438                                 if (marker->second.action == mark_action::stop)
439                                 {
440                                         timeline_frame_number_.set(marker->first);
441                                         frame_fraction_ = 0.0;
442                                         paused_ = true;
443                                 }
444                                 else if (marker->second.action == mark_action::jump_to)
445                                 {
446                                         go_to_marker(marker->second.label_argument, 0);
447                                 }
448                         }
449                         else
450                         {
451                                 timeline_frame_number_.set(next_frame);
452                                 frame_fraction_ -= delta;
453                         }
454
455                         going_to_mark_ = false;
456                 }
457         }
458
459         draw_frame render_progressive_frame()
460         {
461                 if (removed_)
462                         return draw_frame::empty();
463
464                 mouse_x_.set(m_x_);
465                 mouse_y_.set(m_y_);
466
467                 if (!paused_)
468                         advance();
469
470                 frame_number_.set(frame_number_.get() + speed_.get());
471
472                 for (auto& timeline : timelines_)
473                         timeline.second.on_frame(timeline_frame_number_.get());
474
475                 std::vector<draw_frame> frames;
476
477                 for (auto& layer : layers_)
478                 {
479                         if (layer.hidden.get())
480                                 continue;
481
482                         draw_frame frame(layer.producer.get()->receive());
483                         frame.transform() = get_transform(layer);
484                         frames.push_back(frame);
485                 }
486
487                 return draw_frame(frames);
488         }
489
490         void on_interaction(const interaction_event::ptr& event)
491         {
492                 aggregator_.translate_and_send(event);
493         }
494
495         bool collides(double x, double y) const
496         {
497                 m_x_ = static_cast<int64_t>(x * pixel_constraints_.width.get());
498                 m_y_ = static_cast<int64_t>(y * pixel_constraints_.height.get());
499
500                 return static_cast<bool>((collission_detect(x, y)));
501         }
502
503         boost::optional<interaction_target> collission_detect(double x, double y) const
504         {
505                 for (auto& layer : layers_ | boost::adaptors::reversed)
506                 {
507                         if (layer.hidden.get())
508                                 continue;
509
510                         auto transform = get_transform(layer);
511                         auto translated = translate(x, y, transform);
512
513                         if (translated.first >= 0.0
514                                 && translated.first <= 1.0
515                                 && translated.second >= 0.0
516                                 && translated.second <= 1.0
517                                 && layer.producer.get()->collides(translated.first, translated.second))
518                         {
519                                 return std::make_pair(transform, static_cast<interaction_sink*>(layer.producer.get().get()));
520                         }
521                 }
522
523                 return boost::optional<interaction_target>();
524         }
525
526         std::future<std::wstring> call(const std::vector<std::wstring>& params)
527         {
528                 if (!params.empty() && boost::ends_with(params.at(0), L"()"))
529                         return make_ready_future(handle_call(params));
530                 else
531                         return make_ready_future(handle_variable_set(params));
532         }
533
534         std::wstring handle_variable_set(const std::vector<std::wstring>& params)
535         {
536                 for (int i = 0; i + 1 < params.size(); i += 2)
537                 {
538                         auto found = variables_.find(boost::to_lower_copy(params.at(i)));
539
540                         if (found != variables_.end() && found->second->is_public())
541                                 found->second->from_string(params.at(i + 1));
542                 }
543
544                 return L"";
545         }
546
547         std::wstring handle_call(const std::vector<std::wstring>& params)
548         {
549                 auto call = params.at(0);
550
551                 if (call == L"play()")
552                         go_to_marker(params.at(1), -1);
553                 else if (call == L"remove()")
554                         remove();
555                 else if (call == L"next()")
556                         next();
557                 else
558                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Unknown call " + call));
559
560                 return L"";
561         }
562
563         void remove()
564         {
565                 removed_ = true;
566                 layers_.clear();
567         }
568
569         void next()
570         {
571                 auto marker = find_first_start(timeline_frame_number_.get() + 1);
572
573                 if (marker)
574                 {
575                         timeline_frame_number_.set(marker->first - 1);
576                         frame_fraction_ = 0.0;
577                         paused_ = false;
578                         going_to_mark_ = true;
579                 }
580                 else
581                 {
582                         remove();
583                 }
584         }
585
586         void go_to_marker(const std::wstring& marker_name, int64_t offset)
587         {
588                 for (auto& marker : markers_by_frame_)
589                 {
590                         if (marker.second.label_argument == marker_name && marker.second.action == mark_action::start)
591                         {
592                                 timeline_frame_number_.set(marker.first + offset);
593                                 frame_fraction_ = 0.0;
594                                 paused_ = false;
595                                 going_to_mark_ = true;
596
597                                 return;
598                         }
599                 }
600
601                 if (marker_name == L"intro")
602                 {
603                         timeline_frame_number_.set(offset);
604                         frame_fraction_ = 0.0;
605                         paused_ = false;
606                         going_to_mark_ = true;
607                 }
608                 else if (marker_name == L"outro")
609                 {
610                         remove();
611                 }
612                 else
613                         CASPAR_LOG(info) << print() << L" no marker called " << marker_name << " found";
614         }
615
616         std::wstring print() const
617         {
618                 return L"scene[type=" + name() + L" template=" + template_name_ + L"]";
619         }
620
621         std::wstring name() const
622         {
623                 return producer_name_;
624         }
625
626         boost::property_tree::wptree info() const
627         {
628                 boost::property_tree::wptree info;
629                 info.add(L"type", L"scene");
630                 info.add(L"producer-name", name());
631                 info.add(L"template-name", template_name_);
632                 info.add(L"frame-number", frame_number_.get());
633                 info.add(L"timeline-frame-number", timeline_frame_number_.get());
634
635                 for (auto& var : variables_)
636                 {
637                         boost::property_tree::wptree variable_info;
638
639                         variable_info.add(L"name", var.first);
640                         variable_info.add(L"public", var.second->is_public());
641                         variable_info.add(L"value", var.second->to_string());
642
643                         info.add_child(L"variables.variable", variable_info);
644                 }
645
646                 for (auto& layer : layers_)
647                 {
648                         boost::property_tree::wptree layer_info;
649
650                         layer_info.add(L"name", layer.name.get());
651                         layer_info.add_child(L"producer", layer.producer.get()->info());
652                         layer_info.add(L"x", layer.position.x.get());
653                         layer_info.add(L"y", layer.position.y.get());
654                         layer_info.add(L"width", layer.producer.get()->pixel_constraints().width.get());
655                         layer_info.add(L"height", layer.producer.get()->pixel_constraints().height.get());
656
657                         info.add_child(L"layers.layer", layer_info);
658                 }
659
660                 return info;
661         }
662
663         monitor::subject& monitor_output()
664         {
665                 return monitor_subject_;
666         }
667 };
668
669 scene_producer::scene_producer(std::wstring producer_name, std::wstring template_name, int width, int height, const video_format_desc& format_desc)
670         : impl_(new impl(std::move(producer_name), std::move(template_name), width, height, format_desc))
671 {
672 }
673
674 scene_producer::~scene_producer()
675 {
676 }
677
678 layer& scene_producer::create_layer(
679                 const spl::shared_ptr<frame_producer>& producer, int x, int y, const std::wstring& name)
680 {
681         return impl_->create_layer(producer, x, y, name);
682 }
683
684 layer& scene_producer::create_layer(
685                 const spl::shared_ptr<frame_producer>& producer, const std::wstring& name)
686 {
687         return impl_->create_layer(producer, 0, 0, name);
688 }
689
690 void scene_producer::reverse_layers()
691 {
692         impl_->reverse_layers();
693 }
694
695 layer& scene_producer::get_layer(const std::wstring& name)
696 {
697         return impl_->get_layer(name);
698 }
699
700 binding<int64_t> scene_producer::timeline_frame()
701 {
702         return impl_->timeline_frame();
703 }
704
705 draw_frame scene_producer::receive_impl()
706 {
707         return impl_->render_frame();
708 }
709
710 constraints& scene_producer::pixel_constraints() { return impl_->pixel_constraints_; }
711
712 void scene_producer::on_interaction(const interaction_event::ptr& event)
713 {
714         impl_->on_interaction(event);
715 }
716
717 bool scene_producer::collides(double x, double y) const
718 {
719         return impl_->collides(x, y);
720 }
721
722 std::wstring scene_producer::print() const
723 {
724         return impl_->print();
725 }
726
727 std::wstring scene_producer::name() const
728 {
729         return impl_->name();
730 }
731
732 boost::property_tree::wptree scene_producer::info() const
733 {
734         return impl_->info();
735 }
736
737 std::future<std::wstring> scene_producer::call(const std::vector<std::wstring>& params)
738 {
739         return impl_->call(params);
740 }
741
742 monitor::subject& scene_producer::monitor_output()
743 {
744         return impl_->monitor_output();
745 }
746
747 void scene_producer::store_keyframe(void* timeline_identity, const keyframe& k)
748 {
749         impl_->store_keyframe(timeline_identity, k);
750 }
751
752 void scene_producer::store_variable(
753                 const std::wstring& name, const std::shared_ptr<core::variable>& var)
754 {
755         impl_->store_variable(name, var);
756 }
757
758 void scene_producer::add_mark(int64_t frame, mark_action action, const std::wstring& label)
759 {
760         impl_->add_mark(frame, action, label);
761 }
762
763 void scene_producer::add_task(binding<bool> when, std::function<void ()> task)
764 {
765         impl_->add_task(std::move(when), std::move(task));
766 }
767
768 core::variable& scene_producer::get_variable(const std::wstring& name)
769 {
770         return impl_->get_variable(name);
771 }
772
773 const std::vector<std::wstring>& scene_producer::get_variables() const
774 {
775         return impl_->get_variables();
776 }
777
778 }}}