]> git.sesse.net Git - casparcg/commitdiff
Merged ffmpeg duration column and media_info_repository in CLS and CINF from master...
authorHelge Norberg <helge.norberg@svt.se>
Fri, 27 Mar 2015 14:02:48 +0000 (15:02 +0100)
committerHelge Norberg <helge.norberg@svt.se>
Fri, 27 Mar 2015 14:02:48 +0000 (15:02 +0100)
22 files changed:
core/CMakeLists.txt
core/producer/media_info/in_memory_media_info_repository.cpp [new file with mode: 0644]
core/producer/media_info/in_memory_media_info_repository.h [new file with mode: 0644]
core/producer/media_info/media_info.h [new file with mode: 0644]
core/producer/media_info/media_info_repository.h [new file with mode: 0644]
core/thumbnail_generator.cpp
core/thumbnail_generator.h
modules/ffmpeg/ffmpeg.cpp
modules/ffmpeg/ffmpeg.h
modules/ffmpeg/producer/util/util.cpp
modules/ffmpeg/producer/util/util.h
modules/flash/flash.cpp
modules/flash/flash.h
modules/image/image.cpp
modules/image/image.h
protocol/amcp/AMCPCommandsImpl.cpp
protocol/amcp/AMCPCommandsImpl.h
protocol/amcp/AMCPProtocolStrategy.cpp
protocol/amcp/AMCPProtocolStrategy.h
shell/main.cpp
shell/server.cpp
shell/server.h

index 2b507a16ec8bd2cfd1b4a638374ad9055e494fcf..cd11417454ee636554c750522f75c78230759cd3 100644 (file)
@@ -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 (file)
index 0000000..662dc0d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "../../StdAfx.h"
+
+#include "in_memory_media_info_repository.h"
+
+#include <map>
+#include <vector>
+
+#include <boost/thread/mutex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+
+#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<std::wstring, boost::optional<media_info>> info_by_file_;
+       std::vector<media_info_extractor> extractors_;
+public:
+       virtual void register_extractor(media_info_extractor extractor) override
+       {
+               boost::lock_guard<boost::mutex> lock(mutex_);
+
+               extractors_.push_back(extractor);
+       }
+
+       virtual boost::optional<media_info> get(const std::wstring& file) override
+       {
+               boost::lock_guard<boost::mutex> 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<media_info> 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<boost::mutex> lock(mutex_);
+
+               info_by_file_.erase(file);
+       }
+};
+
+spl::shared_ptr<struct media_info_repository> create_in_memory_media_info_repository()
+{
+       return spl::make_shared<in_memory_media_info_repository>();
+}
+
+}}
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 (file)
index 0000000..81264fe
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <string>
+
+#include <common/memory.h>
+
+namespace caspar { namespace core {
+
+spl::shared_ptr<struct media_info_repository> 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 (file)
index 0000000..a298e9b
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+#include <boost/rational.hpp>
+
+namespace caspar { namespace core {
+
+struct media_info
+{
+       std::int64_t                                    duration        = 0;
+       boost::rational<std::int64_t>   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 (file)
index 0000000..963c6fa
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <string>
+#include <functional>
+
+#include <boost/optional.hpp>
+
+namespace caspar { namespace core {
+
+struct media_info;
+typedef std::function<bool (
+               const std::wstring& file,
+               const std::wstring& upper_case_extension,
+               media_info& info)
+> media_info_extractor;
+
+struct media_info_repository
+{
+       virtual ~media_info_repository() { }
+       virtual void register_extractor(media_info_extractor extractor) = 0;
+       virtual boost::optional<media_info> get(const std::wstring& file) = 0;
+       virtual void remove(const std::wstring& file) = 0;
+};
+
+}}
index 53758fdf2796151d2a55ab88eb54c551dd88f815..3bced675e6a289613ba9884cc1a67b87330c231c 100644 (file)
@@ -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> image_mixer_;
-       spl::shared_ptr<diagnostics::graph> graph_;
-       video_format_desc format_desc_;
-       spl::unique_ptr<thumbnail_output> 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>                    image_mixer_;
+       spl::shared_ptr<diagnostics::graph>             graph_;
+       video_format_desc                                               format_desc_;
+       spl::unique_ptr<thumbnail_output>               output_;
+       mixer                                                                   mixer_;
+       thumbnail_creator                                               thumbnail_creator_;
+       spl::shared_ptr<media_info_repository>  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> image_mixer,
                        int generate_delay_millis,
-                       const thumbnail_creator& thumbnail_creator)
+                       const thumbnail_creator& thumbnail_creator,
+                       spl::shared_ptr<media_info_repository> media_info_repo)
                : media_path_(media_path)
                , thumbnails_path_(thumbnails_path)
                , width_(width)
@@ -127,6 +131,7 @@ public:
                , output_(spl::make_unique<thumbnail_output>(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> image_mixer,
                int generate_delay_millis,
-               const thumbnail_creator& thumbnail_creator)
+               const thumbnail_creator& thumbnail_creator,
+               spl::shared_ptr<media_info_repository> 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))
 {
 }
 
index 7cf3f3e60efdbf321ea379107d7118f4167c01a7..dc99f61558ac044a5c3243a46b53f6646714ad2d 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <boost/noncopyable.hpp>
 
-#include "memory.h"
+#include <common/memory.h>
 #include <common/filesystem_monitor.h>
 
 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<void (
                const class const_frame& frame,
@@ -51,7 +52,8 @@ public:
                        const video_format_desc& render_video_mode,
                        std::unique_ptr<class image_mixer> image_mixer,
                        int generate_delay_millis,
-                       const thumbnail_creator& thumbnail_creator);
+                       const thumbnail_creator& thumbnail_creator,
+                       spl::shared_ptr<media_info_repository> media_info_repo);
        ~thumbnail_generator();
        void generate(const std::wstring& media_file);
        void generate_all();
index 9f5e7ba826107bbb4d72267991948c1aa2fcd4d6..9a5d650f44245d0a6c0aa85a46781e5fd77bf5c6 100644 (file)
 
 #include "consumer/ffmpeg_consumer.h"
 #include "producer/ffmpeg_producer.h"
+#include "producer/util/util.h"
 
 #include <common/log.h>
 
 #include <core/consumer/frame_consumer.h>
 #include <core/producer/frame_producer.h>
+#include <core/producer/media_info/media_info.h>
+#include <core/producer/media_info/media_info_repository.h>
 
 #include <tbb/recursive_mutex.h>
 
@@ -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<core::media_info_repository>& 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<std::wstring>& 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()
index f0eb9006c8ab2b21f73e7740efe143ed043b547a..1119f0033c1d7ee7ad1582791abd06d2ba40573d 100644 (file)
 
 #include <string>
 
-namespace caspar { namespace ffmpeg {
+#include <common/memory.h>
 
-void init();
+namespace caspar {
+namespace core {
+
+struct media_info_repository;
+
+}
+
+namespace ffmpeg {
+
+void init(const spl::shared_ptr<core::media_info_repository>& media_info_repo);
 void uninit();
 
 std::wstring avcodec_version();
index b1c730aa0f31ca74ccb99662633449198c14b95e..e953e4acf47b87c48939cad66253fb67a6eb0f57 100644 (file)
@@ -44,6 +44,7 @@
 #include <common/assert.h>
 #include <boost/filesystem.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/rational.hpp>
 
 #include <fstream>
 
@@ -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<std::int64_t>& time_base)
+{
+       AVFormatContext* weak_context = nullptr;
+       if (avformat_open_input(&weak_context, u8(filename).c_str(), nullptr, nullptr) < 0)
+               return false;
+
+       std::shared_ptr<AVFormatContext> 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<std::int64_t>(static_cast<int>(fps * AV_TIME_BASE), AV_TIME_BASE);
+
+       duration = boost::rational_cast<std::int64_t>(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);
index 4df2d9638d8692bdf82e99cf4f3281236e2f1a6e..eb03224b802d2bf7a656c0855adbfcd7b4839bac 100644 (file)
@@ -28,6 +28,8 @@
 #include <core/frame/pixel_format.h>
 #include <core/mixer/audio/audio_mixer.h>
 
+#include <boost/rational.hpp>
+
 #include <array>
 
 #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<std::int64_t>& time_base);
 
 }}
index b059736bf12170d55ae1f4da77fa62410dc66c66..753ec25a1c46535efd145f4b3f2b6e0ac9093195 100644 (file)
 #include "producer/flash_producer.h"
 
 #include <common/env.h>
+#include <common/os/windows/windows.h>
 
-#ifdef WIN32 
-#include <Windows.h>
-#endif
+#include <core/producer/media_info/media_info.h>
+#include <core/producer/media_info/media_info_repository.h>
+
+#include <string>
 
 namespace caspar { namespace flash {
 
-void init()
+void init(const spl::shared_ptr<core::media_info_repository>& 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()
index 0b6562111a0a5ce8d2f6e3240bd61de141629b59..945ebb359fde5a68139bcb666863fa31fa86da16 100644 (file)
 
 #include <string>
 
-namespace caspar { namespace flash {
+#include <common/memory.h>
 
-void init();
+namespace caspar {
+
+namespace core {
+
+struct media_info_repository;
+
+}
+
+namespace flash {
+
+void init(const spl::shared_ptr<core::media_info_repository>& media_info_repo);
 
 std::wstring cg_version();
 std::wstring version();
index 5d0552e2b546f938614b3789f778b116fe28784e..d912afe5d05224fc360a545315a7d2ce65c19dd2 100644 (file)
@@ -27,6 +27,8 @@
 
 #include <core/producer/frame_producer.h>
 #include <core/consumer/frame_consumer.h>
+#include <core/producer/media_info/media_info.h>
+#include <core/producer/media_info/media_info_repository.h>
 
 #include <common/utf.h>
 
 
 namespace caspar { namespace image {
 
-void init()
+void init(const spl::shared_ptr<core::media_info_repository>& 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<std::wstring>& 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<std::wstring>& params){return create_consumer(params);});
 }
 
 
index fb9e19fdbf4023110f910887cd433dfc88449c51..8a7655ef4f68d321bd6c6f1f23c64d1a7a4063ef 100644 (file)
 
 #include <string>
 
-namespace caspar { namespace image {
+#include <common/memory.h>
 
-void init();
+namespace caspar {
+
+namespace core {
+
+struct media_info_repository;
+
+}
+
+namespace image {
+
+void init(const spl::shared_ptr<core::media_info_repository>& repo);
 void uninit();
 
 std::wstring version();
index ae34cb268954fb74fd2ef31f65d22457cad6cd17..48f323ba3fa93a69071e459241debcbcdcae9f14 100644 (file)
@@ -44,6 +44,8 @@
 #include <core/mixer/mixer.h>
 #include <core/consumer/output.h>
 #include <core/thumbnail_generator.h>
+#include <core/producer/media_info/media_info.h>
+#include <core/producer/media_info/media_info_repository.h>
 #include <core/diagnostics/call_context.h>
 #include <core/diagnostics/osd_graph.h>
 
 #include <modules/flash/producer/cg_proxy.h>
 #include <modules/ffmpeg/producer/util/util.h>
 #include <modules/screen/screen.h>
-#include <modules/reroute/producer/reroute_producer.h>
 
 #include <algorithm>
 #include <locale>
 #include <fstream>
 #include <memory>
 #include <cctype>
-//#include <io.h>
 #include <future>
 
 #include <boost/date_time/posix_time/posix_time.hpp>
@@ -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_repository>& 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<std::wstring>(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<std::wstring>(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<std::wstring>(media_info->duration) +
+               + L" " + boost::lexical_cast<std::wstring>(media_info->time_base.numerator()) + L"/" + boost::lexical_cast<std::wstring>(media_info->time_base.denominator())
+               + L"\r\n";
 }
 
-std::wstring ListMedia()
+std::wstring ListMedia(const spl::shared_ptr<media_info_repository>& 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()));
        }
index d6ad32637fbbcdf1aaf727cde0a5af431beedcb6..a4763ae1e117183f211c138407ea99af8822f5ef 100644 (file)
 * Author: Nicklas P Andersson
 */
 
-#ifndef __AMCPCOMMANDSIMPL_H__
-#define __AMCPCOMMANDSIMPL_H__
+#pragma once 
 
 #include "AMCPCommand.h"
 
 #include <core/thumbnail_generator.h>
+#include <core/producer/media_info/media_info_repository.h>
 
 #include <future>
 
-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<core::media_info_repository>& repo)
+               : AMCPCommandBase(client)
+               , repo_(repo)
+       {}
        std::wstring print() const { return L"ClsCommand";}
        bool DoExecute();
+private:
+       spl::shared_ptr<core::media_info_repository> 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<core::media_info_repository>& repo)
+               : AMCPCommandBase(client)
+               , repo_(repo)
+       {}
        std::wstring print() const { return L"CinfCommand";}
        bool DoExecute();
+private:
+       spl::shared_ptr<core::media_info_repository> 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
index 74f451df4a51bdac1767eecb0efe9badb1f2b661..d6dce4ad24f3ec8e6cb442ac3780eb4baca746b5 100644 (file)
@@ -51,13 +51,21 @@ using IO::ClientInfoPtr;
 struct AMCPProtocolStrategy::impl
 {
 private:
-       std::vector<channel_context>                            channels_;
-       std::vector<AMCPCommandQueue::ptr_type>         commandQueues_;
-       std::shared_ptr<core::thumbnail_generator>      thumb_gen_;
-       std::promise<bool>&                                                     shutdown_server_now_;
+       std::vector<channel_context>                                    channels_;
+       std::vector<AMCPCommandQueue::ptr_type>                 commandQueues_;
+       std::shared_ptr<core::thumbnail_generator>              thumb_gen_;
+       spl::shared_ptr<core::media_info_repository>    media_info_repo_;
+       std::promise<bool>&                                                             shutdown_server_now_;
 
 public:
-       impl(const std::vector<spl::shared_ptr<core::video_channel>>& channels, const std::shared_ptr<core::thumbnail_generator>& thumb_gen, std::promise<bool>& shutdown_server_now) : thumb_gen_(thumb_gen), shutdown_server_now_(shutdown_server_now)
+       impl(
+                       const std::vector<spl::shared_ptr<core::video_channel>>& channels,
+                       const std::shared_ptr<core::thumbnail_generator>& thumb_gen,
+                       const spl::shared_ptr<core::media_info_repository>& media_info_repo,
+                       std::promise<bool>& 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<AMCPCommandQueue>());
 
@@ -350,9 +358,9 @@ private:
                if(s == L"DIAG")                                return std::make_shared<DiagnosticsCommand>(client);
                else if(s == L"CHANNEL_GRID")   return std::make_shared<ChannelGridCommand>(client, channels_);
                else if(s == L"DATA")                   return std::make_shared<DataCommand>(client);
-               else if(s == L"CINF")                   return std::make_shared<CinfCommand>(client);
+               else if(s == L"CINF")                   return std::make_shared<CinfCommand>(client, media_info_repo_);
                else if(s == L"INFO")                   return std::make_shared<InfoCommand>(client, channels_);
-               else if(s == L"CLS")                    return std::make_shared<ClsCommand>(client);
+               else if(s == L"CLS")                    return std::make_shared<ClsCommand>(client, media_info_repo_);
                else if(s == L"TLS")                    return std::make_shared<TlsCommand>(client);
                else if(s == L"VERSION")                return std::make_shared<VersionCommand>(client);
                else if(s == L"BYE")                    return std::make_shared<ByeCommand>(client);
@@ -389,7 +397,14 @@ private:
 };
 
 
-AMCPProtocolStrategy::AMCPProtocolStrategy(const std::vector<spl::shared_ptr<core::video_channel>>& channels, const std::shared_ptr<core::thumbnail_generator>& thumb_gen, std::promise<bool>& shutdown_server_now) : impl_(spl::make_unique<impl>(channels, thumb_gen, shutdown_server_now)) {}
+AMCPProtocolStrategy::AMCPProtocolStrategy(
+               const std::vector<spl::shared_ptr<core::video_channel>>& channels,
+               const std::shared_ptr<core::thumbnail_generator>& thumb_gen,
+               const spl::shared_ptr<core::media_info_repository>& media_info_repo,
+               std::promise<bool>& shutdown_server_now)
+       : impl_(spl::make_unique<impl>(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); }
 
index fe6f0f91e2438acf12ca2159911f9e8d593a1901..7afd59254e92e26f0fb5fcce93aca7a594d201d1 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <core/video_channel.h>
 #include <core/thumbnail_generator.h>
+#include <core/producer/media_info/media_info_repository.h>
 
 #include <common/memory.h>
 
@@ -41,6 +42,7 @@ public:
        AMCPProtocolStrategy(
                const std::vector<spl::shared_ptr<core::video_channel>>& channels, 
                const std::shared_ptr<core::thumbnail_generator>& thumb_gen,
+               const spl::shared_ptr<core::media_info_repository>& media_info_repo,
                std::promise<bool>& shutdown_server_now);
 
        virtual ~AMCPProtocolStrategy();
index 9a76452e2cc50a3c0ea1e5438ac1285aa8200ca3..233740eddcefda47f3a3029f610f356aa2799f37 100644 (file)
@@ -189,7 +189,14 @@ void do_run(server& caspar_server, std::promise<bool>& shutdown_server_now)
        
        // Create a amcp parser for console commands.
        //protocol::amcp::AMCPProtocolStrategy amcp(caspar_server.channels());
-       auto amcp = spl::make_shared<caspar::IO::delimiter_based_chunking_strategy_factory<wchar_t>>(L"\r\n", spl::make_shared<caspar::IO::legacy_strategy_adapter_factory>(spl::make_shared<protocol::amcp::AMCPProtocolStrategy>(caspar_server.channels(), caspar_server.get_thumbnail_generator(), shutdown_server_now)))->create(console_client);
+       auto amcp = spl::make_shared<caspar::IO::delimiter_based_chunking_strategy_factory<wchar_t>>(
+                       L"\r\n",
+                       spl::make_shared<caspar::IO::legacy_strategy_adapter_factory>(
+                                       spl::make_shared<protocol::amcp::AMCPProtocolStrategy>(
+                                                       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)
index cb55a52889192b073e0ec0d9342be467010169cd..ba8ef8aca18d7d7e130dd0ec82a38201e099451e 100644 (file)
@@ -39,6 +39,9 @@
 #include <core/producer/text/text_producer.h>
 #include <core/consumer/output.h>
 #include <core/thumbnail_generator.h>
+#include <core/producer/media_info/media_info.h>
+#include <core/producer/media_info/media_info_repository.h>
+#include <core/producer/media_info/in_memory_media_info_repository.h>
 #include <core/diagnostics/subject_diagnostics.h>
 #include <core/diagnostics/call_context.h>
 #include <core/diagnostics/osd_graph.h>
@@ -73,6 +76,8 @@
 #include <boost/property_tree/ptree.hpp>
 #include <boost/property_tree/xml_parser.hpp>
 
+#include <tbb/atomic.h>
+
 #include <future>
 
 namespace caspar {
@@ -91,18 +96,23 @@ struct server::impl : boost::noncopyable
        osc::client                                                                                     osc_client_;
        std::vector<std::shared_ptr<void>>                                      predefined_osc_subscriptions_;
        std::vector<spl::shared_ptr<video_channel>>                     channels_;
+       spl::shared_ptr<media_info_repository>                          media_info_repo_;
+       boost::thread                                                                           initial_media_info_thread_;
+       tbb::atomic<bool>                                                                       running_;
        std::shared_ptr<thumbnail_generator>                            thumbnail_generator_;
        std::promise<bool>&                                                                     shutdown_server_now_;
 
        explicit impl(std::promise<bool>& 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<amcp::AMCPProtocolStrategy>(channels_, thumbnail_generator_, shutdown_server_now_));
+                       return wrap_legacy_protocol("\r\n", spl::make_shared<amcp::AMCPProtocolStrategy>(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<cii::CIIProtocolStrategy>(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<bool>& shutdown_server_now) : impl_(new impl(shutdown_server_now)){}
@@ -326,6 +363,7 @@ const std::vector<spl::shared_ptr<video_channel>> server::channels() const
        return impl_->channels_;
 }
 std::shared_ptr<core::thumbnail_generator> server::get_thumbnail_generator() const {return impl_->thumbnail_generator_; }
+spl::shared_ptr<media_info_repository> server::get_media_info_repo() const { return impl_->media_info_repo_; }
 core::monitor::subject& server::monitor_output() { return *impl_->monitor_subject_; }
 
 }
index aa6b993402df5b4edde44af11bbf89237d3e455d..6f21f7d5a7e8af55dcee3b17a69f874760c22d3e 100644 (file)
@@ -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<bool>& shutdown_server_now);
        const std::vector<spl::shared_ptr<core::video_channel>> channels() const;
        std::shared_ptr<core::thumbnail_generator> get_thumbnail_generator() const;
+       spl::shared_ptr<core::media_info_repository> get_media_info_repo() const;
 
        core::monitor::subject& monitor_output();
 private: