From 7246d2b6051dce6f9c17a848cf79d526861ba5c0 Mon Sep 17 00:00:00 2001 From: Helge Norberg Date: Tue, 1 Mar 2016 17:48:10 +0100 Subject: [PATCH] Simplified thumbnail creation, making it less intrusive in the frame_producer design. --- core/producer/frame_producer.cpp | 65 ++++--- core/producer/frame_producer.h | 7 +- .../producer/separated/separated_producer.cpp | 14 -- core/thumbnail_generator.cpp | 29 +--- modules/ffmpeg/ffmpeg.cpp | 3 +- modules/ffmpeg/producer/ffmpeg_producer.cpp | 164 ++++++++---------- modules/ffmpeg/producer/ffmpeg_producer.h | 4 +- modules/image/image.cpp | 3 +- modules/image/producer/image_producer.cpp | 18 +- modules/image/producer/image_producer.h | 2 +- 10 files changed, 120 insertions(+), 189 deletions(-) diff --git a/core/producer/frame_producer.cpp b/core/producer/frame_producer.cpp index fa458be44..2b78c6177 100644 --- a/core/producer/frame_producer.cpp +++ b/core/producer/frame_producer.cpp @@ -43,7 +43,7 @@ namespace caspar { namespace core { struct frame_producer_registry::impl { std::vector producer_factories; - std::vector thumbnail_factories; + std::vector thumbnail_producers; spl::shared_ptr help_repo; impl(spl::shared_ptr help_repo) @@ -63,9 +63,9 @@ void frame_producer_registry::register_producer_factory(std::wstring name, const impl_->help_repo->register_item({ L"producer" }, std::move(name), describer); } -void frame_producer_registry::register_thumbnail_producer_factory(const producer_factory_t& factory) +void frame_producer_registry::register_thumbnail_producer(const thumbnail_producer_t& thumbnail_producer) { - impl_->thumbnail_factories.push_back(factory); + impl_->thumbnail_producers.push_back(thumbnail_producer); } frame_producer_dependencies::frame_producer_dependencies( @@ -127,10 +127,6 @@ struct frame_producer_base::impl { return draw_frame::still(last_frame_); } - draw_frame create_thumbnail_frame() - { - return draw_frame::empty(); - } }; frame_producer_base::frame_producer_base() : impl_(new impl(*this)) @@ -151,10 +147,6 @@ draw_frame frame_producer_base::last_frame() { return impl_->last_frame(); } -draw_frame frame_producer_base::create_thumbnail_frame() -{ - return impl_->create_thumbnail_frame(); -} std::future frame_producer_base::call(const std::vector&) { @@ -201,7 +193,6 @@ const spl::shared_ptr& frame_producer::empty() variable& get_variable(const std::wstring& name) override { CASPAR_THROW_EXCEPTION(not_implemented()); } const std::vector& get_variables() const override { static std::vector empty; return empty; } draw_frame last_frame() {return draw_frame::empty();} - draw_frame create_thumbnail_frame() {return draw_frame::empty();} constraints& pixel_constraints() override { static constraints c; return c; } boost::property_tree::wptree info() const override @@ -292,7 +283,6 @@ public: void leading_producer(const spl::shared_ptr& producer) override {return producer_->leading_producer(producer);} uint32_t nb_frames() const override {return producer_->nb_frames();} draw_frame last_frame() {return producer_->last_frame();} - draw_frame create_thumbnail_frame() {return producer_->create_thumbnail_frame();} monitor::subject& monitor_output() override {return producer_->monitor_output();} bool collides(double x, double y) const override {return producer_->collides(x, y);} void on_interaction(const interaction_event::ptr& event) override {return producer_->on_interaction(event);} @@ -339,35 +329,38 @@ spl::shared_ptr do_create_producer(const frame_producer_de return producer; } -spl::shared_ptr frame_producer_registry::create_thumbnail_producer(const frame_producer_dependencies& dependencies, const std::wstring& media_file) const +draw_frame do_create_thumbnail_frame( + const frame_producer_dependencies& dependencies, + const std::wstring& media_file, + const std::vector& thumbnail_producers) +{ + for (auto& thumbnail_producer : thumbnail_producers) + { + auto frame = thumbnail_producer(dependencies, media_file); + + if (frame != draw_frame::empty()) + return frame; + } + + return draw_frame::empty(); +} + +draw_frame frame_producer_registry::create_thumbnail(const frame_producer_dependencies& dependencies, const std::wstring& media_file) const { - auto& thumbnail_factories = impl_->thumbnail_factories; + auto& thumbnail_producers = impl_->thumbnail_producers; std::vector params; params.push_back(media_file); - auto producer = do_create_producer(dependencies, params, thumbnail_factories, true); - auto key_producer = frame_producer::empty(); - - try // to find a key file. - { - auto params_copy = params; - if (params_copy.size() > 0) - { - params_copy[0] += L"_A"; - key_producer = do_create_producer(dependencies, params_copy, thumbnail_factories, true); - if (key_producer == frame_producer::empty()) - { - params_copy[0] += L"LPHA"; - key_producer = do_create_producer(dependencies, params_copy, thumbnail_factories, true); - } - } - } - catch(...){} + auto fill_frame = do_create_thumbnail_frame(dependencies, media_file, thumbnail_producers); + auto key_frame = do_create_thumbnail_frame(dependencies, media_file + L"_A", thumbnail_producers); - if (producer != frame_producer::empty() && key_producer != frame_producer::empty()) - return create_separated_producer(producer, key_producer); + if (key_frame == draw_frame::empty()) + key_frame = do_create_thumbnail_frame(dependencies, media_file + L"_ALPHA", thumbnail_producers); - return producer; + if (fill_frame != draw_frame::empty() && key_frame != draw_frame::empty()) + return draw_frame::mask(fill_frame, key_frame); + + return fill_frame; } spl::shared_ptr frame_producer_registry::create_producer(const frame_producer_dependencies& dependencies, const std::vector& params) const diff --git a/core/producer/frame_producer.h b/core/producer/frame_producer.h index fff718658..cf7fb402a 100644 --- a/core/producer/frame_producer.h +++ b/core/producer/frame_producer.h @@ -96,7 +96,6 @@ public: virtual uint32_t nb_frames() const = 0; virtual uint32_t frame_number() const = 0; virtual draw_frame last_frame() = 0; - virtual draw_frame create_thumbnail_frame() = 0; virtual constraints& pixel_constraints() = 0; virtual void leading_producer(const spl::shared_ptr&) {} }; @@ -121,7 +120,6 @@ public: uint32_t nb_frames() const override; uint32_t frame_number() const override; virtual draw_frame last_frame() override; - virtual draw_frame create_thumbnail_frame() override; private: virtual draw_frame receive() override; @@ -149,16 +147,17 @@ struct frame_producer_dependencies }; typedef std::function(const frame_producer_dependencies&, const std::vector&)> producer_factory_t; +typedef std::function thumbnail_producer_t; class frame_producer_registry : boost::noncopyable { public: frame_producer_registry(spl::shared_ptr help_repo); void register_producer_factory(std::wstring name, const producer_factory_t& factory, const help_item_describer& describer); // Not thread-safe. - void register_thumbnail_producer_factory(const producer_factory_t& factory); // Not thread-safe. + void register_thumbnail_producer(const thumbnail_producer_t& thumbnail_producer); // Not thread-safe. spl::shared_ptr create_producer(const frame_producer_dependencies&, const std::vector& params) const; spl::shared_ptr create_producer(const frame_producer_dependencies&, const std::wstring& params) const; - spl::shared_ptr create_thumbnail_producer(const frame_producer_dependencies&, const std::wstring& media_file) const; + draw_frame create_thumbnail(const frame_producer_dependencies&, const std::wstring& media_file) const; private: struct impl; spl::shared_ptr impl_; diff --git a/core/producer/separated/separated_producer.cpp b/core/producer/separated/separated_producer.cpp index c376eb8e5..ddcf22931 100644 --- a/core/producer/separated/separated_producer.cpp +++ b/core/producer/separated/separated_producer.cpp @@ -88,20 +88,6 @@ public: return draw_frame::mask(fill_producer_->last_frame(), key_producer_->last_frame()); } - draw_frame create_thumbnail_frame() - { - auto fill_frame = fill_producer_->create_thumbnail_frame(); - auto key_frame = key_producer_->create_thumbnail_frame(); - - if (fill_frame == draw_frame::empty() || key_frame == draw_frame::empty()) - return draw_frame::empty(); - - if (fill_frame == draw_frame::late() || key_frame == draw_frame::late()) - return draw_frame::late(); - - return draw_frame::mask(fill_frame, key_frame); - } - constraints& pixel_constraints() override { return fill_producer_->pixel_constraints(); diff --git a/core/thumbnail_generator.cpp b/core/thumbnail_generator.cpp index da39a75e1..a80702c8a 100644 --- a/core/thumbnail_generator.cpp +++ b/core/thumbnail_generator.cpp @@ -246,31 +246,6 @@ public: std::promise thumbnail_ready; { - auto producer = frame_producer::empty(); - - try - { - producer = producer_registry_->create_thumbnail_producer( - frame_producer_dependencies(image_mixer_, { }, format_desc_, producer_registry_), - media_file.wstring()); - } - catch (const boost::thread_interrupted&) - { - throw; - } - catch (...) - { - CASPAR_LOG_CURRENT_EXCEPTION_AT_LEVEL(trace); - CASPAR_LOG(info) << L"Thumbnail producer failed to initialize for " << media_file_with_extension << L". Turn on log level trace to see more information."; - return; - } - - if (producer == frame_producer::empty()) - { - CASPAR_LOG(debug) << L"No appropriate thumbnail producer found for " << media_file_with_extension; - return; - } - boost::filesystem::create_directories(png_file.parent_path()); output_->on_send = [this, &png_file] (const_frame frame) { @@ -282,7 +257,7 @@ public: try { - raw_frame = producer->create_thumbnail_frame(); + raw_frame = producer_registry_->create_thumbnail(frame_producer_dependencies(image_mixer_, {}, format_desc_, producer_registry_), media_file.wstring()); media_info_repo_->remove(file.wstring()); media_info_repo_->get(file.wstring()); } @@ -300,7 +275,7 @@ public: if (raw_frame == draw_frame::empty() || raw_frame == draw_frame::late()) { - CASPAR_LOG(debug) << L"No thumbnail generated for " << media_file_with_extension; + CASPAR_LOG(debug) << L"No thumbnail producer for " << media_file_with_extension; return; } diff --git a/modules/ffmpeg/ffmpeg.cpp b/modules/ffmpeg/ffmpeg.cpp index ca64f7baf..7d8159238 100644 --- a/modules/ffmpeg/ffmpeg.cpp +++ b/modules/ffmpeg/ffmpeg.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -298,7 +299,7 @@ void init(core::module_dependencies dependencies) dependencies.consumer_registry->register_preconfigured_consumer_factory(L"file", create_preconfigured_consumer); dependencies.consumer_registry->register_preconfigured_consumer_factory(L"stream", create_preconfigured_streaming_consumer); dependencies.producer_registry->register_producer_factory(L"FFmpeg Producer", boost::bind(&create_producer, _1, _2, info_repo), describe_producer); - dependencies.producer_registry->register_thumbnail_producer_factory(boost::bind(&create_thumbnail_producer, _1, _2, info_repo)); + dependencies.producer_registry->register_thumbnail_producer(boost::bind(&create_thumbnail_frame, _1, _2, info_repo)); info_repo->register_extractor( [](const std::wstring& file, const std::wstring& extension, core::media_info& info) -> bool diff --git a/modules/ffmpeg/producer/ffmpeg_producer.cpp b/modules/ffmpeg/producer/ffmpeg_producer.cpp index aed7a41dc..fef784877 100644 --- a/modules/ffmpeg/producer/ffmpeg_producer.cpp +++ b/modules/ffmpeg/producer/ffmpeg_producer.cpp @@ -237,74 +237,6 @@ public: return frame; } - core::draw_frame render_specific_frame(uint32_t file_position) - { - muxer_->clear(); - input_.seek(file_position); - - decode_next_frame(); - - if (muxer_->empty()) - { - CASPAR_LOG(trace) << print() << " Giving up finding frame at " << file_position; - - return core::draw_frame::empty(); - } - - auto frame = muxer_->front(); - muxer_->pop(); - - return frame; - } - - core::draw_frame create_thumbnail_frame() override - { - auto quiet_logging = temporary_enable_quiet_logging_for_thread(true); - - auto total_frames = nb_frames(); - auto grid = env::properties().get(L"configuration.thumbnails.video-grid", 2); - - if (grid < 1) - { - CASPAR_LOG(error) << L"configuration/thumbnails/video-grid cannot be less than 1"; - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("configuration/thumbnails/video-grid cannot be less than 1")); - } - - if (grid == 1) - { - return render_specific_frame(total_frames / 2); - } - - auto num_snapshots = grid * grid; - - std::vector frames; - - for (int i = 0; i < num_snapshots; ++i) - { - int x = i % grid; - int y = i / grid; - int desired_frame; - - if (i == 0) - desired_frame = 0; // first - else if (i == num_snapshots - 1) - desired_frame = total_frames - 1; // last - else - // evenly distributed across the file. - desired_frame = total_frames * i / (num_snapshots - 1); - - auto frame = render_specific_frame(desired_frame); - frame.transform().image_transform.fill_scale[0] = 1.0 / static_cast(grid); - frame.transform().image_transform.fill_scale[1] = 1.0 / static_cast(grid); - frame.transform().image_transform.fill_translation[0] = 1.0 / static_cast(grid) * x; - frame.transform().image_transform.fill_translation[1] = 1.0 / static_cast(grid) * y; - - frames.push_back(frame); - } - - return core::draw_frame(frames); - } - core::draw_frame last_frame() override { end_seek(); @@ -581,36 +513,80 @@ spl::shared_ptr create_producer( info))); } -spl::shared_ptr create_thumbnail_producer( - const core::frame_producer_dependencies& dependencies, - const std::vector& params, - const spl::shared_ptr& info_repo) +core::draw_frame create_thumbnail_frame( + const core::frame_producer_dependencies& dependencies, + const std::wstring& media_file, + const spl::shared_ptr& info_repo) { auto quiet_logging = temporary_enable_quiet_logging_for_thread(true); - auto filename = probe_stem(env::media_folder() + L"/" + params.at(0), true); + auto filename = probe_stem(env::media_folder() + L"/" + media_file, true); - if(filename.empty()) - return core::frame_producer::empty(); - - bool loop = false; - auto start = 0; - auto length = std::numeric_limits::max(); - auto filter_str = L""; - auto channel_layout = L""; - bool thumbnail_mode = true; - auto info = info_repo->get(filename); + if (filename.empty()) + return core::draw_frame::empty(); - return spl::make_shared_ptr(std::make_shared( - dependencies.frame_factory, - dependencies.format_desc, - channel_layout, - filename, - filter_str, - loop, - start, - length, - thumbnail_mode, - info)); + auto render_specific_frame = [&](std::int64_t frame_num) + { + spl::shared_ptr producer = spl::make_shared( + dependencies.frame_factory, + dependencies.format_desc, + L"", + filename, + L"", + false, + static_cast(frame_num), + std::numeric_limits::max(), + true, + info_repo->get(filename)); + return producer->receive(); + }; + + auto info = info_repo->get(filename); + + if (!info) + return core::draw_frame::empty(); + + auto total_frames = info->duration; + auto grid = env::properties().get(L"configuration.thumbnails.video-grid", 2); + + if (grid < 1) + { + CASPAR_LOG(error) << L"configuration/thumbnails/video-grid cannot be less than 1"; + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("configuration/thumbnails/video-grid cannot be less than 1")); + } + + if (grid == 1) + { + return render_specific_frame(total_frames / 2); + } + + auto num_snapshots = grid * grid; + + std::vector frames; + + for (int i = 0; i < num_snapshots; ++i) + { + int x = i % grid; + int y = i / grid; + std::int64_t desired_frame; + + if (i == 0) + desired_frame = 0; // first + else if (i == num_snapshots - 1) + desired_frame = total_frames - 30; // last + else + // evenly distributed across the file. + desired_frame = total_frames * i / (num_snapshots - 1); + + auto frame = render_specific_frame(desired_frame); + frame.transform().image_transform.fill_scale[0] = 1.0 / static_cast(grid); + frame.transform().image_transform.fill_scale[1] = 1.0 / static_cast(grid); + frame.transform().image_transform.fill_translation[0] = 1.0 / static_cast(grid) * x; + frame.transform().image_transform.fill_translation[1] = 1.0 / static_cast(grid) * y; + + frames.push_back(frame); + } + + return core::draw_frame(frames); } }} diff --git a/modules/ffmpeg/producer/ffmpeg_producer.h b/modules/ffmpeg/producer/ffmpeg_producer.h index a197be16b..78a1e75d8 100644 --- a/modules/ffmpeg/producer/ffmpeg_producer.h +++ b/modules/ffmpeg/producer/ffmpeg_producer.h @@ -35,9 +35,9 @@ spl::shared_ptr create_producer( const core::frame_producer_dependencies& dependencies, const std::vector& params, const spl::shared_ptr& info_repo); -spl::shared_ptr create_thumbnail_producer( +core::draw_frame create_thumbnail_frame( const core::frame_producer_dependencies& dependencies, - const std::vector& params, + const std::wstring& media_file, const spl::shared_ptr& info_repo); }} \ No newline at end of file diff --git a/modules/image/image.cpp b/modules/image/image.cpp index ea27ca9ce..a03b01e24 100644 --- a/modules/image/image.cpp +++ b/modules/image/image.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -49,7 +50,7 @@ void init(core::module_dependencies dependencies) FreeImage_Initialise(); dependencies.producer_registry->register_producer_factory(L"Image Scroll Producer", create_scroll_producer, describe_scroll_producer); dependencies.producer_registry->register_producer_factory(L"Image Producer", create_producer, describe_producer); - dependencies.producer_registry->register_thumbnail_producer_factory(create_thumbnail_producer); + dependencies.producer_registry->register_thumbnail_producer(create_thumbnail); dependencies.consumer_registry->register_consumer_factory(L"Image Consumer", create_consumer, describe_consumer); dependencies.media_info_repo->register_extractor([](const std::wstring& file, const std::wstring& extension, core::media_info& info) { diff --git a/modules/image/producer/image_producer.cpp b/modules/image/producer/image_producer.cpp index 70902fd1d..0da1a981c 100644 --- a/modules/image/producer/image_producer.cpp +++ b/modules/image/producer/image_producer.cpp @@ -126,11 +126,6 @@ struct image_producer : public core::frame_producer_base return frame_; } - core::draw_frame create_thumbnail_frame() override - { - return frame_; - } - core::constraints& pixel_constraints() override { return constraints_; @@ -277,9 +272,9 @@ spl::shared_ptr create_producer(const core::frame_producer } -spl::shared_ptr create_thumbnail_producer(const core::frame_producer_dependencies& dependencies, const std::vector& params) +core::draw_frame create_thumbnail(const core::frame_producer_dependencies& dependencies, const std::wstring& media_file) { - std::wstring filename = env::media_folder() + params.at(0); + std::wstring filename = env::media_folder() + media_file; auto ext = std::find_if(g_extensions.begin(), g_extensions.end(), [&](const std::wstring& ex) -> bool { @@ -289,9 +284,14 @@ spl::shared_ptr create_thumbnail_producer(const core::fram }); if (ext == g_extensions.end()) - return core::frame_producer::empty(); + return core::draw_frame::empty(); - return spl::make_shared(dependencies.frame_factory, *caspar::find_case_insensitive(filename + *ext), true); + spl::shared_ptr producer = spl::make_shared( + dependencies.frame_factory, + *caspar::find_case_insensitive(filename + *ext), + true); + + return producer->receive(); } }} diff --git a/modules/image/producer/image_producer.h b/modules/image/producer/image_producer.h index d60de6315..eb5af7d29 100644 --- a/modules/image/producer/image_producer.h +++ b/modules/image/producer/image_producer.h @@ -30,6 +30,6 @@ namespace caspar { namespace image { void describe_producer(core::help_sink& sink, const core::help_repository& repo); spl::shared_ptr create_producer(const core::frame_producer_dependencies& dependencies, const std::vector& params); -spl::shared_ptr create_thumbnail_producer(const core::frame_producer_dependencies& dependencies, const std::vector& params); +core::draw_frame create_thumbnail(const core::frame_producer_dependencies& dependencies, const std::wstring& media_file); }} \ No newline at end of file -- 2.39.2