From: Helge Norberg Date: Fri, 27 Mar 2015 14:02:48 +0000 (+0100) Subject: Merged ffmpeg duration column and media_info_repository in CLS and CINF from master... X-Git-Tag: 2.1.0_Beta1~378 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=15b3dad6661649f53562bcea673ba62e605d2b0a;p=casparcg Merged ffmpeg duration column and media_info_repository in CLS and CINF from master, and abstracted CLIP_TYPE as well, so that the CLS command does not need to know about media types. --- diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 2b507a16e..cd1141745 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -25,6 +25,8 @@ set(SOURCES producer/draw/freehand_producer.cpp + producer/media_info/in_memory_media_info_repository.cpp + producer/scene/const_producer.cpp producer/scene/expression_parser.cpp producer/scene/hotswap_producer.cpp @@ -78,6 +80,10 @@ set(HEADERS producer/draw/freehand_producer.h + producer/media_info/in_memory_media_info_repository.h + producer/media_info/media_info.h + producer/media_info/media_info_repository.h + producer/scene/const_producer.h producer/scene/expression_parser.h producer/scene/hotswap_producer.h @@ -125,6 +131,7 @@ source_group(sources\\frame frame/*) source_group(sources\\interaction interaction/*) source_group(sources\\mixer mixer/*) source_group(sources\\producer\\draw producer/draw/*) +source_group(sources\\producer\\media_info producer/media_info/*) source_group(sources\\producer\\scene producer/scene/*) source_group(sources\\producer\\text\\utils producer/text/utils/*) source_group(sources\\producer\\text producer/text/*) diff --git a/core/producer/media_info/in_memory_media_info_repository.cpp b/core/producer/media_info/in_memory_media_info_repository.cpp new file mode 100644 index 000000000..662dc0d98 --- /dev/null +++ b/core/producer/media_info/in_memory_media_info_repository.cpp @@ -0,0 +1,98 @@ +/* +* Copyright 2013 Sveriges Television AB http://casparcg.com/ +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Helge Norberg, helge.norberg@svt.se +*/ + +#include "../../StdAfx.h" + +#include "in_memory_media_info_repository.h" + +#include +#include + +#include +#include +#include +#include + +#include "media_info.h" +#include "media_info_repository.h" + +namespace caspar { namespace core { + +class in_memory_media_info_repository : public media_info_repository +{ + boost::mutex mutex_; + std::map> info_by_file_; + std::vector extractors_; +public: + virtual void register_extractor(media_info_extractor extractor) override + { + boost::lock_guard lock(mutex_); + + extractors_.push_back(extractor); + } + + virtual boost::optional get(const std::wstring& file) override + { + boost::lock_guard lock(mutex_); + + auto iter = info_by_file_.find(file); + + if (iter == info_by_file_.end()) + { + media_info info; + auto extension = boost::to_upper_copy(boost::filesystem::path(file).extension().wstring()); + bool success = false; + + BOOST_FOREACH(auto& extractor, extractors_) + { + success = extractor(file, extension, info); + + if (success) + break; + } + + boost::optional result; + + if (success) + result = info; + + info_by_file_.insert(std::make_pair(file, result)); + + return result; + } + + return iter->second; + } + + virtual void remove(const std::wstring& file) override + { + boost::lock_guard lock(mutex_); + + info_by_file_.erase(file); + } +}; + +spl::shared_ptr create_in_memory_media_info_repository() +{ + return spl::make_shared(); +} + +}} diff --git a/core/producer/media_info/in_memory_media_info_repository.h b/core/producer/media_info/in_memory_media_info_repository.h new file mode 100644 index 000000000..81264fe78 --- /dev/null +++ b/core/producer/media_info/in_memory_media_info_repository.h @@ -0,0 +1,32 @@ +/* +* Copyright 2013 Sveriges Television AB http://casparcg.com/ +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Helge Norberg, helge.norberg@svt.se +*/ + +#pragma once + +#include + +#include + +namespace caspar { namespace core { + +spl::shared_ptr create_in_memory_media_info_repository(); + +}} diff --git a/core/producer/media_info/media_info.h b/core/producer/media_info/media_info.h new file mode 100644 index 000000000..a298e9b6d --- /dev/null +++ b/core/producer/media_info/media_info.h @@ -0,0 +1,42 @@ +/* +* Copyright 2013 Sveriges Television AB http://casparcg.com/ +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Helge Norberg, helge.norberg@svt.se +*/ + +#pragma once + +#include +#include + +#include + +namespace caspar { namespace core { + +struct media_info +{ + std::int64_t duration = 0; + boost::rational time_base; + std::wstring clip_type; + + media_info() + { + } +}; + +}} diff --git a/core/producer/media_info/media_info_repository.h b/core/producer/media_info/media_info_repository.h new file mode 100644 index 000000000..963c6fa34 --- /dev/null +++ b/core/producer/media_info/media_info_repository.h @@ -0,0 +1,46 @@ +/* +* Copyright 2013 Sveriges Television AB http://casparcg.com/ +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Helge Norberg, helge.norberg@svt.se +*/ + +#pragma once + +#include +#include + +#include + +namespace caspar { namespace core { + +struct media_info; +typedef std::function media_info_extractor; + +struct media_info_repository +{ + virtual ~media_info_repository() { } + virtual void register_extractor(media_info_extractor extractor) = 0; + virtual boost::optional get(const std::wstring& file) = 0; + virtual void remove(const std::wstring& file) = 0; +}; + +}} diff --git a/core/thumbnail_generator.cpp b/core/thumbnail_generator.cpp index 53758fdf2..3bced675e 100644 --- a/core/thumbnail_generator.cpp +++ b/core/thumbnail_generator.cpp @@ -44,6 +44,8 @@ #include "frame/frame.h" #include "frame/draw_frame.h" #include "frame/frame_transform.h" +#include "producer/media_info/media_info.h" +#include "producer/media_info/media_info_repository.h" namespace caspar { namespace core { @@ -96,17 +98,18 @@ struct thumbnail_output struct thumbnail_generator::impl { private: - boost::filesystem::wpath media_path_; - boost::filesystem::wpath thumbnails_path_; - int width_; - int height_; - spl::shared_ptr image_mixer_; - spl::shared_ptr graph_; - video_format_desc format_desc_; - spl::unique_ptr output_; - mixer mixer_; - thumbnail_creator thumbnail_creator_; - filesystem_monitor::ptr monitor_; + boost::filesystem::wpath media_path_; + boost::filesystem::wpath thumbnails_path_; + int width_; + int height_; + spl::shared_ptr image_mixer_; + spl::shared_ptr graph_; + video_format_desc format_desc_; + spl::unique_ptr output_; + mixer mixer_; + thumbnail_creator thumbnail_creator_; + spl::shared_ptr media_info_repo_; + filesystem_monitor::ptr monitor_; public: impl( filesystem_monitor_factory& monitor_factory, @@ -117,7 +120,8 @@ public: const video_format_desc& render_video_mode, std::unique_ptr image_mixer, int generate_delay_millis, - const thumbnail_creator& thumbnail_creator) + const thumbnail_creator& thumbnail_creator, + spl::shared_ptr media_info_repo) : media_path_(media_path) , thumbnails_path_(thumbnails_path) , width_(width) @@ -127,6 +131,7 @@ public: , output_(spl::make_unique(generate_delay_millis)) , mixer_(graph_, image_mixer_) , thumbnail_creator_(thumbnail_creator) + , media_info_repo_(std::move(media_info_repo)) , monitor_(monitor_factory.create( media_path, filesystem_event::ALL, @@ -211,6 +216,7 @@ public: case filesystem_event::REMOVED: auto relative_without_extension = get_relative_without_extension(file, media_path_); boost::filesystem::remove(thumbnails_path_ / (relative_without_extension + L".png")); + media_info_repo_->remove(file.wstring()); break; } @@ -289,6 +295,8 @@ public: try { raw_frame = producer->create_thumbnail_frame(); + media_info_repo_->remove(file.wstring()); + media_info_repo_->get(file.wstring()); } catch (const boost::thread_interrupted&) { @@ -348,7 +356,8 @@ thumbnail_generator::thumbnail_generator( const video_format_desc& render_video_mode, std::unique_ptr image_mixer, int generate_delay_millis, - const thumbnail_creator& thumbnail_creator) + const thumbnail_creator& thumbnail_creator, + spl::shared_ptr media_info_repo) : impl_(new impl( monitor_factory, media_path, @@ -357,7 +366,8 @@ thumbnail_generator::thumbnail_generator( render_video_mode, std::move(image_mixer), generate_delay_millis, - thumbnail_creator)) + thumbnail_creator, + media_info_repo)) { } diff --git a/core/thumbnail_generator.h b/core/thumbnail_generator.h index 7cf3f3e60..dc99f6155 100644 --- a/core/thumbnail_generator.h +++ b/core/thumbnail_generator.h @@ -23,7 +23,7 @@ #include -#include "memory.h" +#include #include namespace caspar { namespace core { @@ -31,6 +31,7 @@ namespace caspar { namespace core { class ogl_device; class read_frame; struct video_format_desc; +struct media_info_repository; typedef std::function image_mixer, int generate_delay_millis, - const thumbnail_creator& thumbnail_creator); + const thumbnail_creator& thumbnail_creator, + spl::shared_ptr media_info_repo); ~thumbnail_generator(); void generate(const std::wstring& media_file); void generate_all(); diff --git a/modules/ffmpeg/ffmpeg.cpp b/modules/ffmpeg/ffmpeg.cpp index 9f5e7ba82..9a5d650f4 100644 --- a/modules/ffmpeg/ffmpeg.cpp +++ b/modules/ffmpeg/ffmpeg.cpp @@ -23,11 +23,14 @@ #include "consumer/ffmpeg_consumer.h" #include "producer/ffmpeg_producer.h" +#include "producer/util/util.h" #include #include #include +#include +#include #include @@ -195,7 +198,7 @@ void log_callback(void* ptr, int level, const char* fmt, va_list vl) //} //#pragma warning (pop) -void init() +void init(const spl::shared_ptr& media_info_repo) { av_lockmgr_register(ffmpeg_lock_callback); av_log_set_callback(log_callback); @@ -208,6 +211,26 @@ void init() core::register_consumer_factory([](const std::vector& params){return create_consumer(params);}); core::register_producer_factory(create_producer); + + media_info_repo->register_extractor( + [](const std::wstring& file, const std::wstring& extension, core::media_info& info) -> bool + { + // TODO: merge thumbnail generation from 2.0 + //auto disable_logging = temporary_disable_logging_for_thread(true); + if (extension == L".WAV" || extension == L".MP3") + { + info.clip_type = L"AUDIO"; + return true; + } + + if (!is_valid_file(file)) + return false; + + info.clip_type = L"MOVIE"; + + return try_get_duration(file, info.duration, info.time_base); + }); + } void uninit() diff --git a/modules/ffmpeg/ffmpeg.h b/modules/ffmpeg/ffmpeg.h index f0eb9006c..1119f0033 100644 --- a/modules/ffmpeg/ffmpeg.h +++ b/modules/ffmpeg/ffmpeg.h @@ -23,9 +23,18 @@ #include -namespace caspar { namespace ffmpeg { +#include -void init(); +namespace caspar { +namespace core { + +struct media_info_repository; + +} + +namespace ffmpeg { + +void init(const spl::shared_ptr& media_info_repo); void uninit(); std::wstring avcodec_version(); diff --git a/modules/ffmpeg/producer/util/util.cpp b/modules/ffmpeg/producer/util/util.cpp index b1c730aa0..e953e4acf 100644 --- a/modules/ffmpeg/producer/util/util.cpp +++ b/modules/ffmpeg/producer/util/util.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -558,6 +559,34 @@ bool is_valid_file(const std::wstring& filename) return av_probe_input_format2(&pb, true, &score) != nullptr; } +bool try_get_duration(const std::wstring filename, std::int64_t& duration, boost::rational& time_base) +{ + AVFormatContext* weak_context = nullptr; + if (avformat_open_input(&weak_context, u8(filename).c_str(), nullptr, nullptr) < 0) + return false; + + std::shared_ptr context(weak_context, av_close_input_file); + + context->probesize = context->probesize / 10; + context->max_analyze_duration = context->probesize / 10; + + if (avformat_find_stream_info(context.get(), nullptr) < 0) + return false; + + const auto fps = read_fps(*context, 1.0); + + const auto rational_fps = boost::rational(static_cast(fps * AV_TIME_BASE), AV_TIME_BASE); + + duration = boost::rational_cast(context->duration * rational_fps / AV_TIME_BASE); + + if (rational_fps == 0) + return false; + + time_base = 1 / rational_fps; + + return true; +} + std::wstring probe_stem(const std::wstring& stem) { auto stem2 = boost::filesystem::path(stem); diff --git a/modules/ffmpeg/producer/util/util.h b/modules/ffmpeg/producer/util/util.h index 4df2d9638..eb03224b8 100644 --- a/modules/ffmpeg/producer/util/util.h +++ b/modules/ffmpeg/producer/util/util.h @@ -28,6 +28,8 @@ #include #include +#include + #include #if defined(_MSC_VER) @@ -79,5 +81,6 @@ std::wstring print_mode(int width, int height, double fps, bool interlaced); std::wstring probe_stem(const std::wstring& stem); bool is_valid_file(const std::wstring& filename); +bool try_get_duration(const std::wstring filename, std::int64_t& duration, boost::rational& time_base); }} diff --git a/modules/flash/flash.cpp b/modules/flash/flash.cpp index b059736bf..753ec25a1 100644 --- a/modules/flash/flash.cpp +++ b/modules/flash/flash.cpp @@ -27,17 +27,28 @@ #include "producer/flash_producer.h" #include +#include -#ifdef WIN32 -#include -#endif +#include +#include + +#include namespace caspar { namespace flash { -void init() +void init(const spl::shared_ptr& media_info_repo) { core::register_producer_factory(create_ct_producer); core::register_producer_factory(create_swf_producer); + media_info_repo->register_extractor([](const std::wstring& file, const std::wstring& extension, core::media_info& info) + { + if (extension != L".CT" && extension != L".SWF") + return false; + + info.clip_type = L"MOVIE"; + + return true; + }); } std::wstring cg_version() diff --git a/modules/flash/flash.h b/modules/flash/flash.h index 0b6562111..945ebb359 100644 --- a/modules/flash/flash.h +++ b/modules/flash/flash.h @@ -23,9 +23,19 @@ #include -namespace caspar { namespace flash { +#include -void init(); +namespace caspar { + +namespace core { + +struct media_info_repository; + +} + +namespace flash { + +void init(const spl::shared_ptr& media_info_repo); std::wstring cg_version(); std::wstring version(); diff --git a/modules/image/image.cpp b/modules/image/image.cpp index 5d0552e2b..d912afe5d 100644 --- a/modules/image/image.cpp +++ b/modules/image/image.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include @@ -34,21 +36,35 @@ namespace caspar { namespace image { -void init() +void init(const spl::shared_ptr& repo) { FreeImage_Initialise(); core::register_producer_factory(create_scroll_producer); core::register_producer_factory(create_producer); core::register_thumbnail_producer_factory(create_thumbnail_producer); core::register_consumer_factory([](const std::vector& params){return create_consumer(params);}); + repo->register_extractor([](const std::wstring& file, const std::wstring& extension, core::media_info& info) + { + if (extension == L".TGA" + || extension == L".COL" + || extension == L".PNG" + || extension == L".JPEG" + || extension == L".JPG" + || extension == L".GIF" + || extension == L".BMP") + { + info.clip_type = L"STILL"; + + return true; + } + + return false; + }); } void uninit() { FreeImage_DeInitialise(); -// core::register_producer_factory(create_scroll_producer); -// core::register_producer_factory(create_producer); -// core::register_consumer_factory([](const std::vector& params){return create_consumer(params);}); } diff --git a/modules/image/image.h b/modules/image/image.h index fb9e19fdb..8a7655ef4 100644 --- a/modules/image/image.h +++ b/modules/image/image.h @@ -23,9 +23,19 @@ #include -namespace caspar { namespace image { +#include -void init(); +namespace caspar { + +namespace core { + +struct media_info_repository; + +} + +namespace image { + +void init(const spl::shared_ptr& repo); void uninit(); std::wstring version(); diff --git a/protocol/amcp/AMCPCommandsImpl.cpp b/protocol/amcp/AMCPCommandsImpl.cpp index ae34cb268..48f323ba3 100644 --- a/protocol/amcp/AMCPCommandsImpl.cpp +++ b/protocol/amcp/AMCPCommandsImpl.cpp @@ -44,6 +44,8 @@ #include #include #include +#include +#include #include #include @@ -57,14 +59,12 @@ #include #include #include -#include #include #include #include #include #include -//#include #include #include @@ -196,53 +196,47 @@ std::wstring read_file(const boost::filesystem::wpath& file) return read_latin1_file(file); } -std::wstring MediaInfo(const boost::filesystem::path& path) +std::wstring MediaInfo(const boost::filesystem::path& path, const spl::shared_ptr& media_info_repo) { - if(boost::filesystem::is_regular_file(path)) - { - std::wstring clipttype = L"N/A"; - std::wstring extension = boost::to_upper_copy(path.extension().wstring()); - if(extension == L".TGA" || extension == L".COL" || extension == L".PNG" || extension == L".JPEG" || extension == L".JPG" || - extension == L"GIF" || extension == L"BMP") - clipttype = L"STILL"; - else if(extension == L".WAV" || extension == L".MP3") - clipttype = L"AUDIO"; - else if(extension == L"SWF" || extension == L"CT" || extension == L"DV" || extension == L"MOV" || extension == L"MPG" || extension == L"AVI" || caspar::ffmpeg::is_valid_file(path.wstring())) - clipttype = L"MOVIE"; - - if(clipttype != L"N/A") - { - auto is_not_digit = [](char c){ return std::isdigit(c) == 0; }; + if (!boost::filesystem::is_regular_file(path)) + return L""; - auto relativePath = boost::filesystem::path(path.wstring().substr(env::media_folder().size()-1, path.wstring().size())); + auto media_info = media_info_repo->get(path.wstring()); - auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path))); - writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end()); - auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end()); + if (!media_info) + return L""; - auto sizeStr = boost::lexical_cast(boost::filesystem::file_size(path)); - sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end()); - auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end()); - - auto str = relativePath.replace_extension(L"").generic_wstring(); - while(str.size() > 0 && (str[0] == L'\\' || str[0] == L'/')) - str = std::wstring(str.begin() + 1, str.end()); + auto is_not_digit = [](char c){ return std::isdigit(c) == 0; }; - return std::wstring() + L"\"" + str + - + L"\" " + clipttype + - + L" " + sizeStr + - + L" " + writeTimeWStr + - + L"\r\n"; - } - } - return L""; + auto relativePath = boost::filesystem::wpath(path.wstring().substr(env::media_folder().size() - 1, path.wstring().size())); + + auto writeTimeStr = boost::posix_time::to_iso_string(boost::posix_time::from_time_t(boost::filesystem::last_write_time(path))); + writeTimeStr.erase(std::remove_if(writeTimeStr.begin(), writeTimeStr.end(), is_not_digit), writeTimeStr.end()); + auto writeTimeWStr = std::wstring(writeTimeStr.begin(), writeTimeStr.end()); + + auto sizeStr = boost::lexical_cast(boost::filesystem::file_size(path)); + sizeStr.erase(std::remove_if(sizeStr.begin(), sizeStr.end(), is_not_digit), sizeStr.end()); + auto sizeWStr = std::wstring(sizeStr.begin(), sizeStr.end()); + + auto str = relativePath.replace_extension(L"").generic_wstring(); + if (str[0] == '\\' || str[0] == '/') + str = std::wstring(str.begin() + 1, str.end()); + + return std::wstring() + + L"\"" + str + + + L"\" " + media_info->clip_type + + + L" " + sizeStr + + + L" " + writeTimeWStr + + + L" " + boost::lexical_cast(media_info->duration) + + + L" " + boost::lexical_cast(media_info->time_base.numerator()) + L"/" + boost::lexical_cast(media_info->time_base.denominator()) + + L"\r\n"; } -std::wstring ListMedia() +std::wstring ListMedia(const spl::shared_ptr& media_info_repo) { std::wstringstream replyString; - for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr) - replyString << MediaInfo(itr->path()); + for (boost::filesystem::recursive_directory_iterator itr(env::media_folder()), end; itr != end; ++itr) + replyString << MediaInfo(itr->path(), media_info_repo); return boost::to_upper_copy(replyString.str()); } @@ -1454,7 +1448,7 @@ bool CinfCommand::DoExecute() auto path = itr->path(); auto file = path.replace_extension(L"").filename(); if(boost::iequals(file.wstring(), parameters().at(0))) - info += MediaInfo(itr->path()) + L"\r\n"; + info += MediaInfo(itr->path(), repo_) + L"\r\n"; } if(info.empty()) @@ -1632,7 +1626,7 @@ bool ClsCommand::DoExecute() { std::wstringstream replyString; replyString << L"200 CLS OK\r\n"; - replyString << ListMedia(); + replyString << ListMedia(repo_); replyString << L"\r\n"; SetReplyString(boost::to_upper_copy(replyString.str())); } diff --git a/protocol/amcp/AMCPCommandsImpl.h b/protocol/amcp/AMCPCommandsImpl.h index d6ad32637..a4763ae1e 100644 --- a/protocol/amcp/AMCPCommandsImpl.h +++ b/protocol/amcp/AMCPCommandsImpl.h @@ -19,22 +19,16 @@ * Author: Nicklas P Andersson */ - -#ifndef __AMCPCOMMANDSIMPL_H__ -#define __AMCPCOMMANDSIMPL_H__ +#pragma once #include "AMCPCommand.h" #include +#include #include -namespace caspar { namespace protocol { - -std::wstring ListMedia(); -std::wstring ListTemplates(); - -namespace amcp { +namespace caspar { namespace protocol { namespace amcp { class ChannelGridCommand : public AMCPCommandBase<0>, AMCPChannelsAwareCommand { @@ -232,9 +226,14 @@ public: class ClsCommand : public AMCPCommandBase<0> { public: - explicit ClsCommand(IO::ClientInfoPtr client) : AMCPCommandBase(client) {} + explicit ClsCommand(IO::ClientInfoPtr client, const spl::shared_ptr& repo) + : AMCPCommandBase(client) + , repo_(repo) + {} std::wstring print() const { return L"ClsCommand";} bool DoExecute(); +private: + spl::shared_ptr repo_; }; class TlsCommand : public AMCPCommandBase<0> @@ -248,9 +247,14 @@ public: class CinfCommand : public AMCPCommandBase<1> { public: - explicit CinfCommand(IO::ClientInfoPtr client) : AMCPCommandBase(client) {} + explicit CinfCommand(IO::ClientInfoPtr client, const spl::shared_ptr& repo) + : AMCPCommandBase(client) + , repo_(repo) + {} std::wstring print() const { return L"CinfCommand";} bool DoExecute(); +private: + spl::shared_ptr repo_; }; class InfoCommand : public AMCPCommandBase<0>, AMCPChannelsAwareCommand @@ -336,5 +340,3 @@ private: } //namespace amcp }} //namespace caspar - -#endif //__AMCPCOMMANDSIMPL_H__ \ No newline at end of file diff --git a/protocol/amcp/AMCPProtocolStrategy.cpp b/protocol/amcp/AMCPProtocolStrategy.cpp index 74f451df4..d6dce4ad2 100644 --- a/protocol/amcp/AMCPProtocolStrategy.cpp +++ b/protocol/amcp/AMCPProtocolStrategy.cpp @@ -51,13 +51,21 @@ using IO::ClientInfoPtr; struct AMCPProtocolStrategy::impl { private: - std::vector channels_; - std::vector commandQueues_; - std::shared_ptr thumb_gen_; - std::promise& shutdown_server_now_; + std::vector channels_; + std::vector commandQueues_; + std::shared_ptr thumb_gen_; + spl::shared_ptr media_info_repo_; + std::promise& shutdown_server_now_; public: - impl(const std::vector>& channels, const std::shared_ptr& thumb_gen, std::promise& shutdown_server_now) : thumb_gen_(thumb_gen), shutdown_server_now_(shutdown_server_now) + impl( + const std::vector>& channels, + const std::shared_ptr& thumb_gen, + const spl::shared_ptr& media_info_repo, + std::promise& shutdown_server_now) + : thumb_gen_(thumb_gen) + , media_info_repo_(media_info_repo) + , shutdown_server_now_(shutdown_server_now) { commandQueues_.push_back(std::make_shared()); @@ -350,9 +358,9 @@ private: if(s == L"DIAG") return std::make_shared(client); else if(s == L"CHANNEL_GRID") return std::make_shared(client, channels_); else if(s == L"DATA") return std::make_shared(client); - else if(s == L"CINF") return std::make_shared(client); + else if(s == L"CINF") return std::make_shared(client, media_info_repo_); else if(s == L"INFO") return std::make_shared(client, channels_); - else if(s == L"CLS") return std::make_shared(client); + else if(s == L"CLS") return std::make_shared(client, media_info_repo_); else if(s == L"TLS") return std::make_shared(client); else if(s == L"VERSION") return std::make_shared(client); else if(s == L"BYE") return std::make_shared(client); @@ -389,7 +397,14 @@ private: }; -AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector>& channels, const std::shared_ptr& thumb_gen, std::promise& shutdown_server_now) : impl_(spl::make_unique(channels, thumb_gen, shutdown_server_now)) {} +AMCPProtocolStrategy::AMCPProtocolStrategy( + const std::vector>& channels, + const std::shared_ptr& thumb_gen, + const spl::shared_ptr& media_info_repo, + std::promise& shutdown_server_now) + : impl_(spl::make_unique(channels, thumb_gen, media_info_repo, shutdown_server_now)) +{ +} AMCPProtocolStrategy::~AMCPProtocolStrategy() {} void AMCPProtocolStrategy::Parse(const std::wstring& msg, IO::ClientInfoPtr pClientInfo) { impl_->Parse(msg, pClientInfo); } diff --git a/protocol/amcp/AMCPProtocolStrategy.h b/protocol/amcp/AMCPProtocolStrategy.h index fe6f0f91e..7afd59254 100644 --- a/protocol/amcp/AMCPProtocolStrategy.h +++ b/protocol/amcp/AMCPProtocolStrategy.h @@ -25,6 +25,7 @@ #include #include +#include #include @@ -41,6 +42,7 @@ public: AMCPProtocolStrategy( const std::vector>& channels, const std::shared_ptr& thumb_gen, + const spl::shared_ptr& media_info_repo, std::promise& shutdown_server_now); virtual ~AMCPProtocolStrategy(); diff --git a/shell/main.cpp b/shell/main.cpp index 9a76452e2..233740edd 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -189,7 +189,14 @@ void do_run(server& caspar_server, std::promise& shutdown_server_now) // Create a amcp parser for console commands. //protocol::amcp::AMCPProtocolStrategy amcp(caspar_server.channels()); - auto amcp = spl::make_shared>(L"\r\n", spl::make_shared(spl::make_shared(caspar_server.channels(), caspar_server.get_thumbnail_generator(), shutdown_server_now)))->create(console_client); + auto amcp = spl::make_shared>( + L"\r\n", + spl::make_shared( + spl::make_shared( + caspar_server.channels(), + caspar_server.get_thumbnail_generator(), + caspar_server.get_media_info_repo(), + shutdown_server_now)))->create(console_client); std::wstring wcmd; while(true) diff --git a/shell/server.cpp b/shell/server.cpp index cb55a5288..ba8ef8aca 100644 --- a/shell/server.cpp +++ b/shell/server.cpp @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -73,6 +76,8 @@ #include #include +#include + #include namespace caspar { @@ -91,18 +96,23 @@ struct server::impl : boost::noncopyable osc::client osc_client_; std::vector> predefined_osc_subscriptions_; std::vector> channels_; + spl::shared_ptr media_info_repo_; + boost::thread initial_media_info_thread_; + tbb::atomic running_; std::shared_ptr thumbnail_generator_; std::promise& shutdown_server_now_; explicit impl(std::promise& shutdown_server_now) : accelerator_(env::properties().get(L"configuration.accelerator", L"auto")) , osc_client_(io_service_manager_.service()) + , media_info_repo_(create_in_memory_media_info_repository()) , shutdown_server_now_(shutdown_server_now) { + running_ = true; core::diagnostics::osd::register_sink(); diag_subject_->attach_parent(monitor_subject_); - ffmpeg::init(); + ffmpeg::init(media_info_repo_); CASPAR_LOG(info) << L"Initialized ffmpeg module."; bluefish::init(); @@ -117,10 +127,10 @@ struct server::impl : boost::noncopyable screen::init(); CASPAR_LOG(info) << L"Initialized ogl module."; - image::init(); + image::init(media_info_repo_); CASPAR_LOG(info) << L"Initialized image module."; - flash::init(); + flash::init(media_info_repo_); CASPAR_LOG(info) << L"Initialized flash module."; psd::init(); @@ -142,10 +152,15 @@ struct server::impl : boost::noncopyable setup_osc(env::properties()); CASPAR_LOG(info) << L"Initialized osc."; + + start_initial_media_info_scan(); + CASPAR_LOG(info) << L"Started initial media information retrieval."; } ~impl() { + running_ = false; + initial_media_info_thread_.join(); thumbnail_generator_.reset(); primary_amcp_server_.reset(); async_servers_.clear(); @@ -267,7 +282,8 @@ struct server::impl : boost::noncopyable core::video_format_desc(pt.get(L"configuration.thumbnails.video-mode", L"720p2500")), accelerator_.create_image_mixer(), pt.get(L"configuration.thumbnails.generate-delay-millis", 2000), - &image::write_cropped_png)); + &image::write_cropped_png, + media_info_repo_)); CASPAR_LOG(info) << L"Initialized thumbnail generator."; } @@ -306,7 +322,7 @@ struct server::impl : boost::noncopyable using namespace IO; if(boost::iequals(name, L"AMCP")) - return wrap_legacy_protocol("\r\n", spl::make_shared(channels_, thumbnail_generator_, shutdown_server_now_)); + return wrap_legacy_protocol("\r\n", spl::make_shared(channels_, thumbnail_generator_, media_info_repo_, shutdown_server_now_)); else if(boost::iequals(name, L"CII")) return wrap_legacy_protocol("\r\n", spl::make_shared(channels_)); else if(boost::iequals(name, L"CLOCK")) @@ -317,6 +333,27 @@ struct server::impl : boost::noncopyable CASPAR_THROW_EXCEPTION(caspar_exception() << arg_name_info(L"name") << arg_value_info(name) << msg_info(L"Invalid protocol")); } + void start_initial_media_info_scan() + { + initial_media_info_thread_ = boost::thread([this] + { + for (boost::filesystem::wrecursive_directory_iterator iter(env::media_folder()), end; iter != end; ++iter) + { + if (running_) + { + if (boost::filesystem::is_regular_file(iter->path())) + media_info_repo_->get(iter->path().wstring()); + } + else + { + CASPAR_LOG(info) << L"Initial media information retrieval aborted."; + return; + } + } + + CASPAR_LOG(info) << L"Initial media information retrieval finished."; + }); + } }; server::server(std::promise& shutdown_server_now) : impl_(new impl(shutdown_server_now)){} @@ -326,6 +363,7 @@ const std::vector> server::channels() const return impl_->channels_; } std::shared_ptr server::get_thumbnail_generator() const {return impl_->thumbnail_generator_; } +spl::shared_ptr server::get_media_info_repo() const { return impl_->media_info_repo_; } core::monitor::subject& server::monitor_output() { return *impl_->monitor_subject_; } } diff --git a/shell/server.h b/shell/server.h index aa6b99340..6f21f7d5a 100644 --- a/shell/server.h +++ b/shell/server.h @@ -35,6 +35,7 @@ namespace caspar { namespace core { class video_channel; class thumbnail_generator; + struct media_info_repository; } class server final : public boost::noncopyable @@ -43,6 +44,7 @@ public: explicit server(std::promise& shutdown_server_now); const std::vector> channels() const; std::shared_ptr get_thumbnail_generator() const; + spl::shared_ptr get_media_info_repo() const; core::monitor::subject& monitor_output(); private: