X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fscreen%2Fconsumer%2Fscreen_consumer.cpp;h=b992cfb69f12773d79f1be9f959fcd14d1110201;hb=9e4b08cde6c6de9e83a3fff42d90affc3cd8e5bc;hp=b6b6990418a303e18bcfe37f1f86d865911d31aa;hpb=46663f04a90b7a26dbabc1a6519836367a087e5f;p=casparcg diff --git a/modules/screen/consumer/screen_consumer.cpp b/modules/screen/consumer/screen_consumer.cpp index b6b699041..b992cfb69 100644 --- a/modules/screen/consumer/screen_consumer.cpp +++ b/modules/screen/consumer/screen_consumer.cpp @@ -30,29 +30,37 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include + +//#include #include +#include #include #include #include +#include +#include +#include -#include #include #include -#include #include #include +#include #include #include #include -#include - #include #include @@ -62,7 +70,7 @@ #pragma warning (push) #pragma warning (disable : 4244) #endif -extern "C" +extern "C" { #define __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS @@ -74,8 +82,8 @@ extern "C" #endif namespace caspar { namespace screen { - -enum stretch + +enum class stretch { none, uniform, @@ -85,35 +93,23 @@ enum stretch struct configuration { - enum aspect_ratio + enum class aspect_ratio { aspect_4_3 = 0, aspect_16_9, aspect_invalid, }; - - std::wstring name; - int screen_index; - stretch stretch; - bool windowed; - bool auto_deinterlace; - bool key_only; - aspect_ratio aspect; - bool vsync; - bool interactive; - - configuration() - : name(L"ogl") - , screen_index(0) - , stretch(fill) - , windowed(true) - , auto_deinterlace(true) - , key_only(false) - , aspect(aspect_invalid) - , vsync(true) - , interactive(true) - { - } + + std::wstring name = L"Screen consumer"; + int screen_index = 0; + screen::stretch stretch = screen::stretch::fill; + bool windowed = true; + bool auto_deinterlace = true; + bool key_only = false; + aspect_ratio aspect = aspect_ratio::aspect_invalid; + bool vsync = false; + bool interactive = true; + bool borderless = false; }; struct screen_consumer : boost::noncopyable @@ -122,23 +118,25 @@ struct screen_consumer : boost::noncopyable core::video_format_desc format_desc_; int channel_index_; - GLuint texture_; - std::vector pbos_; - + GLuint texture_ = 0; + std::vector pbos_ = std::vector { 0, 0 }; + float width_; float height_; int screen_x_; int screen_y_; - int screen_width_; - int screen_height_; - int square_width_; - int square_height_; + int screen_width_ = format_desc_.width; + int screen_height_ = format_desc_.height; + int square_width_ = format_desc_.square_width; + int square_height_ = format_desc_.square_height; sf::Window window_; + tbb::atomic polling_event_; + std::int64_t pts_; spl::shared_ptr graph_; - boost::timer perf_timer_; - boost::timer tick_timer_; + caspar::timer perf_timer_; + caspar::timer tick_timer_; caspar::prec_timer wait_timer_; @@ -147,6 +145,7 @@ struct screen_consumer : boost::noncopyable boost::thread thread_; tbb::atomic is_running_; + tbb::atomic current_presentation_age_; ffmpeg::filter filter_; public: @@ -154,25 +153,20 @@ public: const configuration& config, const core::video_format_desc& format_desc, int channel_index, - core::interaction_sink* sink) + core::interaction_sink* sink) : config_(config) , format_desc_(format_desc) , channel_index_(channel_index) - , texture_(0) - , pbos_(2, 0) - , screen_width_(format_desc.width) - , screen_height_(format_desc.height) - , square_width_(format_desc.square_width) - , square_height_(format_desc.square_height) + , pts_(0) , sink_(sink) , filter_([&]() -> ffmpeg::filter - { - const auto sample_aspect_ratio = + { + const auto sample_aspect_ratio = boost::rational( - format_desc.square_width, + format_desc.square_width, format_desc.square_height) / boost::rational( - format_desc.width, + format_desc.width, format_desc.height); return ffmpeg::filter( @@ -182,51 +176,57 @@ public: boost::rational(format_desc.time_scale, format_desc.duration), sample_aspect_ratio, AV_PIX_FMT_BGRA, - boost::assign::list_of(AV_PIX_FMT_BGRA), - format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? "" : "YADIF=1:-1"); + { AV_PIX_FMT_BGRA }, + format_desc.field_mode == core::field_mode::progressive || !config.auto_deinterlace ? "" : "format=pix_fmts=gbrp,YADIF=1:-1"); }()) - { - if(format_desc_.format == core::video_format::ntsc && config_.aspect == configuration::aspect_4_3) + { + if (format_desc_.format == core::video_format::ntsc && config_.aspect == configuration::aspect_ratio::aspect_4_3) { // Use default values which are 4:3. } else { - if(config_.aspect == configuration::aspect_16_9) + if (config_.aspect == configuration::aspect_ratio::aspect_16_9) square_width_ = (format_desc.height*16)/9; - else if(config_.aspect == configuration::aspect_4_3) + else if (config_.aspect == configuration::aspect_ratio::aspect_4_3) square_width_ = (format_desc.height*4)/3; } frame_buffer_.set_capacity(1); - - graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); + + graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f)); graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f)); graph_->set_text(print()); diagnostics::register_graph(graph_); - - DISPLAY_DEVICE d_device = {sizeof(d_device), 0}; + + /*DISPLAY_DEVICE d_device = {sizeof(d_device), 0}; std::vector displayDevices; for(int n = 0; EnumDisplayDevices(NULL, n, &d_device, NULL); ++n) displayDevices.push_back(d_device); if(config_.screen_index >= displayDevices.size()) CASPAR_LOG(warning) << print() << L" Invalid screen-index: " << config_.screen_index; - + DEVMODE devmode = {}; if(!EnumDisplaySettings(displayDevices[config_.screen_index].DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) CASPAR_LOG(warning) << print() << L" Could not find display settings for screen-index: " << config_.screen_index; - + screen_x_ = devmode.dmPosition.x; screen_y_ = devmode.dmPosition.y; screen_width_ = config_.windowed ? square_width_ : devmode.dmPelsWidth; - screen_height_ = config_.windowed ? square_height_ : devmode.dmPelsHeight; - + screen_height_ = config_.windowed ? square_height_ : devmode.dmPelsHeight;*/ + screen_x_ = 0; + screen_y_ = 0; + screen_width_ = square_width_; + screen_height_ = square_height_; + + polling_event_ = false; is_running_ = true; + current_presentation_age_ = 0; thread_ = boost::thread([this]{run();}); } - + ~screen_consumer() { is_running_ = false; @@ -236,12 +236,17 @@ public: void init() { - window_.Create(sf::VideoMode(screen_width_, screen_height_, 32), u8(L"Screen consumer " + channel_and_format()), config_.windowed ? sf::Style::Resize | sf::Style::Close : sf::Style::Fullscreen); - window_.ShowMouseCursor(config_.interactive); - window_.SetPosition(screen_x_, screen_y_); - window_.SetSize(screen_width_, screen_height_); - window_.SetActive(); - + auto window_style = config_.borderless + ? sf::Style::None + : (config_.windowed + ? sf::Style::Resize | sf::Style::Close + : sf::Style::Fullscreen); + window_.create(sf::VideoMode(screen_width_, screen_height_, 32), u8(print()), window_style); + window_.setMouseCursorVisible(config_.interactive); + window_.setPosition(sf::Vector2i(screen_x_, screen_y_)); + window_.setSize(sf::Vector2u(screen_width_, screen_height_)); + window_.setActive(); + if(!GLEW_VERSION_2_1 && glewInit() != GLEW_OK) CASPAR_THROW_EXCEPTION(gl::ogl_exception() << msg_info("Failed to initialize GLEW.")); @@ -249,13 +254,13 @@ public: CASPAR_THROW_EXCEPTION(not_supported() << msg_info("Missing OpenGL 2.1 support.")); GL(glEnable(GL_TEXTURE_2D)); - GL(glDisable(GL_DEPTH_TEST)); + GL(glDisable(GL_DEPTH_TEST)); GL(glClearColor(0.0, 0.0, 0.0, 0.0)); GL(glViewport(0, 0, format_desc_.width, format_desc_.height)); GL(glLoadIdentity()); - + calculate_aspect(); - + GL(glGenTextures(1, &texture_)); GL(glBindTexture(GL_TEXTURE_2D, texture_)); GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); @@ -264,36 +269,29 @@ public: GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)); GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0)); GL(glBindTexture(GL_TEXTURE_2D, 0)); - + GL(glGenBuffers(2, pbos_.data())); - + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]); glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]); glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); - - auto wglSwapIntervalEXT = reinterpret_cast(wglGetProcAddress("wglSwapIntervalEXT")); - if(wglSwapIntervalEXT) + + window_.setVerticalSyncEnabled(config_.vsync); + + if (config_.vsync) { - if(config_.vsync) - { - wglSwapIntervalEXT(1); - CASPAR_LOG(info) << print() << " Enabled vsync."; - } - else - wglSwapIntervalEXT(0); + CASPAR_LOG(info) << print() << " Enabled vsync."; } - - CASPAR_LOG(info) << print() << " Successfully Initialized."; } void uninit() - { + { if(texture_) glDeleteTextures(1, &texture_); - BOOST_FOREACH(auto& pbo, pbos_) + for (auto& pbo : pbos_) { if(pbo) glDeleteBuffers(1, &pbo); @@ -302,62 +300,85 @@ public: void run() { + ensure_gpf_handler_installed_for_thread("screen-consumer-thread"); + try { init(); while(is_running_) - { + { try { - sf::Event e; - while(window_.GetEvent(e)) + auto poll_event = [this](sf::Event& e) { - if (e.Type == sf::Event::Resized) + polling_event_ = true; + CASPAR_SCOPE_EXIT + { + polling_event_ = false; + }; + return window_.pollEvent(e); + }; + + sf::Event e; + while(poll_event(e)) + { + if (e.type == sf::Event::Resized) calculate_aspect(); - else if (e.Type == sf::Event::Closed) + else if (e.type == sf::Event::Closed) is_running_ = false; else if (config_.interactive && sink_) { - switch (e.Type) + switch (e.type) { case sf::Event::MouseMoved: { - auto& mouse_move = e.MouseMove; + auto& mouse_move = e.mouseMove; sink_->on_interaction(spl::make_shared( 1, - static_cast(mouse_move.X) / screen_width_, - static_cast(mouse_move.Y) / screen_height_)); + static_cast(mouse_move.x) / screen_width_, + static_cast(mouse_move.y) / screen_height_)); } break; case sf::Event::MouseButtonPressed: case sf::Event::MouseButtonReleased: { - auto& mouse_button = e.MouseButton; + auto& mouse_button = e.mouseButton; sink_->on_interaction(spl::make_shared( 1, - static_cast(mouse_button.X) / screen_width_, - static_cast(mouse_button.Y) / screen_height_, - static_cast(mouse_button.Button), - e.Type == sf::Event::MouseButtonPressed)); + static_cast(mouse_button.x) / screen_width_, + static_cast(mouse_button.y) / screen_height_, + static_cast(mouse_button.button), + e.type == sf::Event::MouseButtonPressed)); + } + break; + case sf::Event::MouseWheelMoved: + { + auto& wheel_moved = e.mouseWheel; + sink_->on_interaction(spl::make_shared( + 1, + static_cast(wheel_moved.x) / screen_width_, + static_cast(wheel_moved.y) / screen_height_, + wheel_moved.delta)); } break; } } } - - auto frame = core::const_frame::empty(); + + core::const_frame frame; frame_buffer_.pop(frame); render_and_draw_frame(frame); - + /*perf_timer_.restart(); render(frame); - graph_->set_value("frame-time", perf_timer_.elapsed()*format_desc_.fps*0.5); + graph_->set_value("frame-time", perf_timer_.elapsed()*format_desc_.fps*0.5); window_.Display();*/ - graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5); + current_presentation_age_ = frame.get_age_millis(); + graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5); tick_timer_.restart(); } catch(...) @@ -388,60 +409,61 @@ public: void wait_for_vblank_and_display() { try_sleep_almost_until_vblank(); - window_.Display(); + window_.display(); // Make sure that the next tick measures the duration from this point in time. wait_timer_.tick(0.0); } spl::shared_ptr get_av_frame() - { - spl::shared_ptr av_frame(avcodec_alloc_frame(), av_free); - avcodec_get_frame_defaults(av_frame.get()); - - av_frame->linesize[0] = format_desc_.width*4; + { + auto av_frame = ffmpeg::create_frame(); + + av_frame->linesize[0] = format_desc_.width*4; av_frame->format = PIX_FMT_BGRA; av_frame->width = format_desc_.width; av_frame->height = format_desc_.height; av_frame->interlaced_frame = format_desc_.field_mode != core::field_mode::progressive; av_frame->top_field_first = format_desc_.field_mode == core::field_mode::upper ? 1 : 0; + av_frame->pts = pts_++; return av_frame; } - void render_and_draw_frame(core::const_frame frame) + void render_and_draw_frame(core::const_frame input_frame) { - if(static_cast(frame.image_data().size()) != format_desc_.size) + if(static_cast(input_frame.image_data().size()) != format_desc_.size) return; if(screen_width_ == 0 && screen_height_ == 0) return; - + perf_timer_.restart(); auto av_frame = get_av_frame(); - av_frame->data[0] = const_cast(frame.image_data().begin()); + av_frame->data[0] = const_cast(input_frame.image_data().begin()); filter_.push(av_frame); - auto frames = filter_.poll_all(); + auto frame = filter_.poll(); - if (frames.empty()) + if (!frame) return; - if (frames.size() == 1) + if (!filter_.is_double_rate()) { - render(frames[0]); + render(spl::make_shared_ptr(frame)); graph_->set_value("frame-time", perf_timer_.elapsed() * format_desc_.fps * 0.5); wait_for_vblank_and_display(); // progressive frame } - else if (frames.size() == 2) + else { - render(frames[0]); + render(spl::make_shared_ptr(frame)); double perf_elapsed = perf_timer_.elapsed(); wait_for_vblank_and_display(); // field1 perf_timer_.restart(); - render(frames[1]); + frame = filter_.poll(); + render(spl::make_shared_ptr(frame)); perf_elapsed += perf_timer_.elapsed(); graph_->set_value("frame-time", perf_elapsed * format_desc_.fps * 0.5); @@ -451,6 +473,7 @@ public: void render(spl::shared_ptr av_frame) { + CASPAR_LOG_CALL(trace) << "screen_consumer::render() <- " << print(); GL(glBindTexture(GL_TEXTURE_2D, texture_)); GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0])); @@ -471,39 +494,35 @@ public: }); } else - { - tbb::parallel_for(tbb::blocked_range(0, format_desc_.height), [&](const tbb::blocked_range& r) - { - for(int n = r.begin(); n != r.end(); ++n) - A_memcpy(ptr+n*format_desc_.width*4, av_frame->data[0]+n*av_frame->linesize[0], format_desc_.width*4); - }); + { + fast_memcpy(ptr, av_frame->data[0], format_desc_.size); } - + GL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)); // release the mapped buffer } GL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)); - - GL(glClear(GL_COLOR_BUFFER_BIT)); + + GL(glClear(GL_COLOR_BUFFER_BIT)); glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex2f(-width_, -height_); glTexCoord2f(1.0f, 1.0f); glVertex2f( width_, -height_); glTexCoord2f(1.0f, 0.0f); glVertex2f( width_, height_); glTexCoord2f(0.0f, 0.0f); glVertex2f(-width_, height_); glEnd(); - + GL(glBindTexture(GL_TEXTURE_2D, 0)); std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end()); } - boost::unique_future send(core::const_frame frame) + std::future send(core::const_frame frame) { - if(!frame_buffer_.try_push(frame)) - graph_->set_tag("dropped-frame"); + if(!frame_buffer_.try_push(frame) && !polling_event_) + graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame"); - return wrap_as_future(is_running_.load()); + return make_ready_future(is_running_.load()); } std::wstring channel_and_format() const @@ -512,32 +531,32 @@ public: } std::wstring print() const - { - return config_.name + channel_and_format(); + { + return config_.name + L" " + channel_and_format(); } - + void calculate_aspect() { if(config_.windowed) { - screen_height_ = window_.GetHeight(); - screen_width_ = window_.GetWidth(); + screen_height_ = window_.getSize().y; + screen_width_ = window_.getSize().x; } - + GL(glViewport(0, 0, screen_width_, screen_height_)); std::pair target_ratio = None(); - if(config_.stretch == fill) + if (config_.stretch == screen::stretch::fill) target_ratio = Fill(); - else if(config_.stretch == uniform) + else if (config_.stretch == screen::stretch::uniform) target_ratio = Uniform(); - else if(config_.stretch == uniform_to_fill) + else if (config_.stretch == screen::stretch::uniform_to_fill) target_ratio = UniformToFill(); width_ = target_ratio.first; height_ = target_ratio.second; } - + std::pair None() { float width = static_cast(square_width_)/static_cast(screen_width_); @@ -588,20 +607,25 @@ public: , sink_(sink) { } - + // frame_consumer - void initialize(const core::video_format_desc& format_desc, int channel_index) override + void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout&, int channel_index) override { consumer_.reset(); consumer_.reset(new screen_consumer(config_, format_desc, channel_index, sink_)); } - - boost::unique_future send(core::const_frame frame) override + + int64_t presentation_frame_age_millis() const override + { + return consumer_ ? static_cast(consumer_->current_presentation_age_) : 0; + } + + std::future send(core::const_frame frame) override { return consumer_->send(frame); } - + std::wstring print() const override { return consumer_ ? consumer_->print() : L"[screen_consumer]"; @@ -619,6 +643,7 @@ public: info.add(L"key-only", config_.key_only); info.add(L"windowed", config_.windowed); info.add(L"auto-deinterlace", config_.auto_deinterlace); + info.add(L"vsync", config_.vsync); return info; } @@ -626,7 +651,7 @@ public: { return false; } - + int buffer_depth() const override { return 1; @@ -641,51 +666,85 @@ public: { return monitor_subject_; } -}; +}; + +void describe_consumer(core::help_sink& sink, const core::help_repository& repo) +{ + sink.short_description(L"Displays the contents of a channel on screen using OpenGL."); + sink.syntax( + L"SCREEN " + L"{[screen_index:int]|1} " + L"{[fullscreen:FULLSCREEN]} " + L"{[borderless:BORDERLESS]} " + L"{[key_only:KEY_ONLY]} " + L"{[non_interactive:NON_INTERACTIVE]} " + L"{[no_auto_deinterlace:NO_AUTO_DEINTERLACE]} " + L"{NAME [name:string]}"); + sink.para()->text(L"Displays the contents of a channel on screen using OpenGL."); + sink.definitions() + ->item(L"screen_index", L"Determines which screen the channel should be displayed on. Defaults to 1.") + ->item(L"fullscreen", L"If specified opens the window in fullscreen.") + ->item(L"borderless", L"Makes the window appear without any window decorations.") + ->item(L"key_only", L"Only displays the alpha channel of the video channel if specified.") + ->item(L"non_interactive", L"If specified does not send mouse input to producers on the video channel.") + ->item(L"no_auto_deinterlace", L"If the video mode of the channel is an interlaced mode, specifying this will turn of deinterlacing.") + ->item(L"name", L"Optionally specifies a name of the window to show."); + sink.para()->text(L"Examples:"); + sink.example(L">> ADD 1 SCREEN", L"opens a screen consumer on the default screen."); + sink.example(L">> ADD 1 SCREEN 2", L"opens a screen consumer on the screen 2."); + sink.example(L">> ADD 1 SCREEN 1 FULLSCREEN", L"opens a screen consumer in fullscreen on screen 1."); + sink.example(L">> ADD 1 SCREEN 1 BORDERLESS", L"opens a screen consumer without borders/window decorations on screen 1."); +} -spl::shared_ptr create_consumer(const std::vector& params) +spl::shared_ptr create_consumer( + const std::vector& params, core::interaction_sink* sink, std::vector> channels) { - if(params.size() < 1 || params[0] != L"SCREEN") + if (params.size() < 1 || !boost::iequals(params.at(0), L"SCREEN")) return core::frame_consumer::empty(); - + configuration config; - - if(params.size() > 1) - config.screen_index = boost::lexical_cast(params[1]); - - config.key_only = std::find(params.begin(), params.end(), L"WINDOWED") != params.end(); - config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end(); - - auto name_it = std::find(params.begin(), params.end(), L"NAME"); - if(name_it != params.end() && ++name_it != params.end()) - config.name = *name_it; - - return spl::make_shared(config, nullptr); + + if (params.size() > 1) + config.screen_index = boost::lexical_cast(params.at(1)); + + config.windowed = !contains_param(L"FULLSCREEN", params); + config.key_only = contains_param(L"KEY_ONLY", params); + config.interactive = !contains_param(L"NON_INTERACTIVE", params); + config.auto_deinterlace = !contains_param(L"NO_AUTO_DEINTERLACE", params); + config.borderless = contains_param(L"BORDERLESS", params); + + if (contains_param(L"NAME", params)) + config.name = get_param(L"NAME", params); + + return spl::make_shared(config, sink); } -spl::shared_ptr create_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink* sink) +spl::shared_ptr create_preconfigured_consumer( + const boost::property_tree::wptree& ptree, core::interaction_sink* sink, std::vector> channels) { configuration config; - config.name = ptree.get(L"name", config.name); - config.screen_index = ptree.get(L"device", config.screen_index+1)-1; - config.windowed = ptree.get(L"windowed", config.windowed); - config.key_only = ptree.get(L"key-only", config.key_only); - config.auto_deinterlace = ptree.get(L"auto-deinterlace", config.auto_deinterlace); - config.vsync = ptree.get(L"vsync", config.vsync); - + config.name = ptree.get(L"name", config.name); + config.screen_index = ptree.get(L"device", config.screen_index + 1) - 1; + config.windowed = ptree.get(L"windowed", config.windowed); + config.key_only = ptree.get(L"key-only", config.key_only); + config.auto_deinterlace = ptree.get(L"auto-deinterlace", config.auto_deinterlace); + config.vsync = ptree.get(L"vsync", config.vsync); + config.interactive = ptree.get(L"interactive", config.interactive); + config.borderless = ptree.get(L"borderless", config.borderless); + auto stretch_str = ptree.get(L"stretch", L"default"); if(stretch_str == L"uniform") - config.stretch = stretch::uniform; + config.stretch = screen::stretch::uniform; else if(stretch_str == L"uniform_to_fill") - config.stretch = stretch::uniform_to_fill; + config.stretch = screen::stretch::uniform_to_fill; auto aspect_str = ptree.get(L"aspect-ratio", L"default"); if(aspect_str == L"16:9") - config.aspect = configuration::aspect_16_9; + config.aspect = configuration::aspect_ratio::aspect_16_9; else if(aspect_str == L"4:3") - config.aspect = configuration::aspect_4_3; - + config.aspect = configuration::aspect_ratio::aspect_4_3; + return spl::make_shared(config, sink); } -}} \ No newline at end of file +}}