struct frame_producer_registry::impl
{
std::vector<producer_factory_t> producer_factories;
- std::vector<producer_factory_t> thumbnail_factories;
+ std::vector<thumbnail_producer_t> thumbnail_producers;
spl::shared_ptr<help_repository> help_repo;
impl(spl::shared_ptr<help_repository> help_repo)
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(
{
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))
{
return impl_->last_frame();
}
-draw_frame frame_producer_base::create_thumbnail_frame()
-{
- return impl_->create_thumbnail_frame();
-}
std::future<std::wstring> frame_producer_base::call(const std::vector<std::wstring>&)
{
variable& get_variable(const std::wstring& name) override { CASPAR_THROW_EXCEPTION(not_implemented()); }
const std::vector<std::wstring>& get_variables() const override { static std::vector<std::wstring> 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
void leading_producer(const spl::shared_ptr<frame_producer>& 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);}
return producer;
}
-spl::shared_ptr<core::frame_producer> 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_producer_t>& 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<std::wstring> 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<core::frame_producer> frame_producer_registry::create_producer(const frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params) const
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<frame_producer>&) {}
};
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;
};
typedef std::function<spl::shared_ptr<core::frame_producer>(const frame_producer_dependencies&, const std::vector<std::wstring>&)> producer_factory_t;
+typedef std::function<draw_frame (const frame_producer_dependencies&, const std::wstring&)> thumbnail_producer_t;
class frame_producer_registry : boost::noncopyable
{
public:
frame_producer_registry(spl::shared_ptr<help_repository> 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<core::frame_producer> create_producer(const frame_producer_dependencies&, const std::vector<std::wstring>& params) const;
spl::shared_ptr<core::frame_producer> create_producer(const frame_producer_dependencies&, const std::wstring& params) const;
- spl::shared_ptr<core::frame_producer> 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> impl_;
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();
std::promise<void> 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)
{
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());
}
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;
}
#include <common/os/general_protection_fault.h>
#include <core/consumer/frame_consumer.h>
+#include <core/frame/draw_frame.h>
#include <core/producer/frame_producer.h>
#include <core/producer/media_info/media_info.h>
#include <core/producer/media_info/media_info_repository.h>
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
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<core::draw_frame> 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<double>(grid);
- frame.transform().image_transform.fill_scale[1] = 1.0 / static_cast<double>(grid);
- frame.transform().image_transform.fill_translation[0] = 1.0 / static_cast<double>(grid) * x;
- frame.transform().image_transform.fill_translation[1] = 1.0 / static_cast<double>(grid) * y;
-
- frames.push_back(frame);
- }
-
- return core::draw_frame(frames);
- }
-
core::draw_frame last_frame() override
{
end_seek();
info)));
}
-spl::shared_ptr<core::frame_producer> create_thumbnail_producer(
- const core::frame_producer_dependencies& dependencies,
- const std::vector<std::wstring>& params,
- const spl::shared_ptr<core::media_info_repository>& info_repo)
+core::draw_frame create_thumbnail_frame(
+ const core::frame_producer_dependencies& dependencies,
+ const std::wstring& media_file,
+ const spl::shared_ptr<core::media_info_repository>& 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<uint32_t>::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<ffmpeg_producer>(
- 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<core::frame_producer> producer = spl::make_shared<ffmpeg_producer>(
+ dependencies.frame_factory,
+ dependencies.format_desc,
+ L"",
+ filename,
+ L"",
+ false,
+ static_cast<uint32_t>(frame_num),
+ std::numeric_limits<uint32_t>::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<core::draw_frame> 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<double>(grid);
+ frame.transform().image_transform.fill_scale[1] = 1.0 / static_cast<double>(grid);
+ frame.transform().image_transform.fill_translation[0] = 1.0 / static_cast<double>(grid) * x;
+ frame.transform().image_transform.fill_translation[1] = 1.0 / static_cast<double>(grid) * y;
+
+ frames.push_back(frame);
+ }
+
+ return core::draw_frame(frames);
}
}}
const core::frame_producer_dependencies& dependencies,
const std::vector<std::wstring>& params,
const spl::shared_ptr<core::media_info_repository>& info_repo);
-spl::shared_ptr<core::frame_producer> create_thumbnail_producer(
+core::draw_frame create_thumbnail_frame(
const core::frame_producer_dependencies& dependencies,
- const std::vector<std::wstring>& params,
+ const std::wstring& media_file,
const spl::shared_ptr<core::media_info_repository>& info_repo);
}}
\ No newline at end of file
#include <core/consumer/frame_consumer.h>
#include <core/producer/media_info/media_info.h>
#include <core/producer/media_info/media_info_repository.h>
+#include <core/frame/draw_frame.h>
#include <core/system_info_provider.h>
#include <common/utf.h>
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)
{
return frame_;
}
- core::draw_frame create_thumbnail_frame() override
- {
- return frame_;
- }
-
core::constraints& pixel_constraints() override
{
return constraints_;
}
-spl::shared_ptr<core::frame_producer> create_thumbnail_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& 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
{
});
if (ext == g_extensions.end())
- return core::frame_producer::empty();
+ return core::draw_frame::empty();
- return spl::make_shared<image_producer>(dependencies.frame_factory, *caspar::find_case_insensitive(filename + *ext), true);
+ spl::shared_ptr<core::frame_producer> producer = spl::make_shared<image_producer>(
+ dependencies.frame_factory,
+ *caspar::find_case_insensitive(filename + *ext),
+ true);
+
+ return producer->receive();
}
}}
void describe_producer(core::help_sink& sink, const core::help_repository& repo);
spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
-spl::shared_ptr<core::frame_producer> create_thumbnail_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
+core::draw_frame create_thumbnail(const core::frame_producer_dependencies& dependencies, const std::wstring& media_file);
}}
\ No newline at end of file