#include "ffmpeg_producer.h"
#include "../ffmpeg_error.h"
+#include "../ffmpeg.h"
#include "muxer/frame_muxer.h"
#include "input/input.h"
#include <core/monitor/monitor.h>
#include <core/help/help_repository.h>
#include <core/help/help_sink.h>
+#include <core/producer/media_info/media_info_repository.h>
+#include <core/producer/media_info/media_info.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
const double fps_ = read_fps(input_.context(), format_desc_.fps);
const uint32_t start_;
+ const bool thumbnail_mode_;
+ const boost::optional<core::media_info> info_;
std::unique_ptr<video_decoder> video_decoder_;
std::unique_ptr<audio_decoder> audio_decoder_;
const std::wstring& filter,
bool loop,
uint32_t start,
- uint32_t length)
+ uint32_t length,
+ bool thumbnail_mode,
+ boost::optional<core::media_info> info)
: filename_(filename)
, frame_factory_(frame_factory)
, format_desc_(format_desc)
- , input_(graph_, filename_, loop, start, length)
- , fps_(read_fps(input_.context(), format_desc_.fps))
+ , input_(graph_, filename_, loop, start, length, thumbnail_mode)
, start_(start)
+ , thumbnail_mode_(thumbnail_mode)
+ , info_(info)
{
graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
graph_->set_color("underflow", diagnostics::color(0.6f, 0.3f, 0.9f));
try
{
- video_decoder_.reset(new video_decoder(input_));
+ video_decoder_.reset(new video_decoder(input_, thumbnail_mode));
video_decoder_->monitor_output().attach_parent(monitor_subject_);
constraints_.width.set(video_decoder_->width());
constraints_.height.set(video_decoder_->height());
- CASPAR_LOG(info) << print() << L" " << video_decoder_->print();
+ if (is_logging_quiet_for_thread())
+ CASPAR_LOG(debug) << print() << L" " << video_decoder_->print();
+ else
+ CASPAR_LOG(info) << print() << L" " << video_decoder_->print();
}
catch(averror_stream_not_found&)
{
- //CASPAR_LOG(warning) << print() << " No video-stream found. Running without video.";
+ CASPAR_LOG(debug) << print() << " No video-stream found. Running without video.";
}
catch(...)
{
auto channel_layout = core::audio_channel_layout::invalid();
- try
+ if (!thumbnail_mode)
{
- audio_decoder_ .reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
- audio_decoder_->monitor_output().attach_parent(monitor_subject_);
+ try
+ {
+ audio_decoder_.reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
+ audio_decoder_->monitor_output().attach_parent(monitor_subject_);
- channel_layout = audio_decoder_->channel_layout();
-
- CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
- }
- catch(averror_stream_not_found&)
- {
- //CASPAR_LOG(warning) << print() << " No audio-stream found. Running without audio.";
- }
- catch(...)
- {
- CASPAR_LOG_CURRENT_EXCEPTION();
- CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";
+ channel_layout = audio_decoder_->channel_layout();
+
+ CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
+ }
+ catch (averror_stream_not_found&)
+ {
+ CASPAR_LOG(debug) << print() << " No audio-stream found. Running without audio.";
+ }
+ catch (...)
+ {
+ CASPAR_LOG_CURRENT_EXCEPTION();
+ CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";
+ }
}
if (start_ > file_nb_frames())
decode_next_frame();
- CASPAR_LOG(info) << print() << L" Initialized";
+ if (is_logging_quiet_for_thread())
+ CASPAR_LOG(debug) << print() << L" Initialized";
+ else
+ CASPAR_LOG(info) << print() << L" Initialized";
}
// frame_producer
uint32_t file_nb_frames() const
{
uint32_t file_nb_frames = 0;
+
+ if (info_)
+ file_nb_frames = static_cast<uint32_t>(info_->duration);
+
file_nb_frames = std::max(file_nb_frames, video_decoder_ ? video_decoder_->nb_frames() : 0);
file_nb_frames = std::max(file_nb_frames, audio_decoder_ ? audio_decoder_->nb_frames() : 0);
return file_nb_frames;
{
for(int n = 0; n < 32 && muxer_->empty(); ++n)
{
- if(!muxer_->video_ready())
- muxer_->push_video(video_decoder_ ? (*video_decoder_)() : create_frame());
- if(!muxer_->audio_ready())
- muxer_->push_audio(audio_decoder_ ? (*audio_decoder_)() : create_frame());
+ std::shared_ptr<AVFrame> video;
+ std::shared_ptr<AVFrame> audio;
+ bool needs_video = !muxer_->video_ready();
+ bool needs_audio = !muxer_->audio_ready();
+
+ tbb::parallel_invoke(
+ [&]
+ {
+ if (needs_video)
+ video = video_decoder_ ? (*video_decoder_)() : create_frame();
+ },
+ [&]
+ {
+ if (needs_audio)
+ audio = audio_decoder_ ? (*audio_decoder_)() : create_frame();
+ });
+
+ muxer_->push_video(video);
+ muxer_->push_audio(audio);
}
graph_->set_text(print());
sink.example(L">> CALL 1-10 LENGTH 50");
}
-spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
-{
- auto filename = probe_stem(env::media_folder() + L"/" + params.at(0));
+spl::shared_ptr<core::frame_producer> create_producer(
+ const core::frame_producer_dependencies& dependencies,
+ const std::vector<std::wstring>& params,
+ const spl::shared_ptr<core::media_info_repository>& info_repo)
+{
+ auto filename = probe_stem(env::media_folder() + L"/" + params.at(0), false);
if(filename.empty())
return core::frame_producer::empty();
auto length = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());
auto filter_str = get_param(L"FILTER", params, L"");
auto channel_layout = get_param(L"CHANNEL_LAYOUT", params, L"");
+ bool thumbnail_mode = false;
+ auto info = info_repo->get(filename);
return create_destroy_proxy(spl::make_shared_ptr(std::make_shared<ffmpeg_producer>(
dependencies.frame_factory,
filter_str,
loop,
start,
- length)));
+ length,
+ thumbnail_mode,
+ info)));
+}
+
+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"/" + media_file, true);
+
+ if (filename.empty())
+ return core::draw_frame::empty();
+
+ 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);
}
}}