]> git.sesse.net Git - casparcg/commitdiff
* Enforce help descriptions for producers in code.
authorHelge Norberg <helge.norberg@svt.se>
Thu, 2 Jul 2015 21:45:41 +0000 (23:45 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Thu, 2 Jul 2015 21:45:41 +0000 (23:45 +0200)
* Added HELP PRODUCER command to list available producers and get help for them.
* Updated generate_docs.cpp to also include a wiki page for the producers.

30 files changed:
core/help/help_repository.cpp
core/producer/frame_producer.cpp
core/producer/frame_producer.h
core/producer/scene/xml_scene_producer.cpp
core/producer/text/text_producer.cpp
core/producer/text/text_producer.h
modules/decklink/decklink.cpp
modules/decklink/producer/decklink_producer.cpp
modules/decklink/producer/decklink_producer.h
modules/ffmpeg/ffmpeg.cpp
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/ffmpeg_producer.h
modules/flash/flash.cpp
modules/flash/producer/flash_producer.cpp
modules/flash/producer/flash_producer.h
modules/html/html.cpp
modules/html/producer/html_producer.cpp
modules/html/producer/html_producer.h
modules/image/image.cpp
modules/image/producer/image_producer.cpp
modules/image/producer/image_producer.h
modules/image/producer/image_scroll_producer.cpp
modules/image/producer/image_scroll_producer.h
modules/psd/psd_scene_producer.cpp
modules/reroute/producer/reroute_producer.cpp
modules/reroute/producer/reroute_producer.h
modules/reroute/reroute.cpp
protocol/amcp/AMCPCommandsImpl.cpp
shell/generate_docs.cpp
shell/server.cpp

index d66f8cb8566834d49dba7f9bd78086c0060b09ed..5b36f580af3426514f0255602caf259d6a93c64e 100644 (file)
@@ -25,6 +25,7 @@
 #include <common/except.h>
 
 #include <boost/range/adaptor/filtered.hpp>
+#include <boost/algorithm/string.hpp>
 
 #include <algorithm>
 #include <utility>
@@ -69,9 +70,9 @@ void help_repository::help(std::set<std::wstring> tags, std::wstring name, help_
 {
        auto found = impl_->items | boost::adaptors::filtered([&](const std::pair<help_item, std::set<std::wstring>>& item)
        {
-               return item.first.first == name && std::includes(
-                       item.second.begin(), item.second.end(),
-                       tags.begin(), tags.end());
+               return boost::iequals(item.first.first, name) && std::includes(
+                               item.second.begin(), item.second.end(),
+                               tags.begin(), tags.end());
        });
 
        if (found.empty())
index 384a019989b859b4e1be090a085337ace580ec12..f5075b782aaef1a19fbc1e5094b5feb27924e755 100644 (file)
@@ -43,18 +43,25 @@ namespace caspar { namespace core {
 
 struct frame_producer_registry::impl
 {
-       std::vector<producer_factory_t> producer_factories;
-       std::vector<producer_factory_t> thumbnail_factories;
+       std::vector<producer_factory_t>         producer_factories;
+       std::vector<producer_factory_t>         thumbnail_factories;
+       spl::shared_ptr<help_repository>        help_repo;
+
+       impl(spl::shared_ptr<help_repository> help_repo)
+               : help_repo(std::move(help_repo))
+       {
+       }
 };
 
-frame_producer_registry::frame_producer_registry()
-    : impl_(new impl)
+frame_producer_registry::frame_producer_registry(spl::shared_ptr<help_repository> help_repo)
+    : impl_(new impl(std::move(help_repo)))
 {
 }
 
-void frame_producer_registry::register_producer_factory(const producer_factory_t& factory)
+void frame_producer_registry::register_producer_factory(std::wstring name, const producer_factory_t& factory, const help_item_describer& describer)
 {
        impl_->producer_factories.push_back(factory);
+       impl_->help_repo->register_item({ L"producer" }, std::move(name), describer);
 }
 
 void frame_producer_registry::register_thumbnail_producer_factory(const producer_factory_t& factory)
index c420ebf6d333d10bc3ebebddf417791379e9581d..0a91db2a9b314994d214f4e59c7559d0f2c0e42c 100644 (file)
@@ -24,6 +24,7 @@
 #include "../monitor/monitor.h"
 #include "../fwd.h"
 #include "../interaction/interaction_sink.h"
+#include "../help/help_repository.h"
 #include "binding.h"
 
 #include <common/forward.h>
@@ -152,8 +153,8 @@ typedef std::function<spl::shared_ptr<core::frame_producer>(const frame_producer
 class frame_producer_registry : boost::noncopyable
 {
 public:
-       frame_producer_registry();
-       void register_producer_factory(const producer_factory_t& factory); // Not thread-safe.
+       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.
        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;
index f9aa4f39de9d54e30135db3af5f4b4d2b0408f79..df95d820d48e72000f875ff5c0cca6f147208be6 100644 (file)
@@ -31,6 +31,8 @@
 
 #include <common/env.h>
 #include <core/producer/frame_producer.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <future>
 
@@ -61,24 +63,15 @@ void deduce_expression(variable& var, const variable_repository& repo)
        }
 }
 
-void init(module_dependencies dependencies)
+void describe_xml_scene_producer(core::help_sink& sink, const core::help_repository& repo)
 {
-       dependencies.producer_registry->register_producer_factory(create_xml_scene_producer);
-       dependencies.cg_registry->register_cg_producer(
-                       L"scene",
-                       { L".scene" },
-                       [](const std::wstring& filename) { return ""; },
-                       [](const spl::shared_ptr<core::frame_producer>& producer)
-                       {
-                               return spl::make_shared<core::scene::scene_cg_proxy>(producer);
-                       },
-                       [](
-                                       const core::frame_producer_dependencies& dependencies,
-                                       const std::wstring& filename)
-                       {
-                               return create_xml_scene_producer(dependencies, { filename });
-                       },
-                       false);
+       sink.short_description(L"A simple producer for dynamic graphics using .scene files.");
+       sink.syntax(L"[.scene_filename:string] {[param1:string] [value1:string]} {[param2:string] [value2:string]} ...");
+       sink.para()->text(L"A simple producer that looks in the ")->code(L"templates")->text(L" folder for .scene files.");
+       sink.para()->text(L"The .scene file is a simple XML document containing variables, layers and timelines.");
+       sink.example(L">> PLAY 1-10 scene_name_sign FIRSTNAME \"John\" LASTNAME \"Doe\"", L"loads and plays templates/scene_name_sign.scene and sets the variables FIRSTNAME and LASTNAME.");
+       sink.para()->text(L"The scene producer also supports setting the variables while playing via the CALL command:");
+       sink.example(L">> CALL 1-10 FIRSTNAME \"Jane\"", L"changes the variable FIRSTNAME on an already loaded scene.");
 }
 
 spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
@@ -225,4 +218,24 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
        return scene;
 }
 
+void init(module_dependencies dependencies)
+{
+       dependencies.producer_registry->register_producer_factory(L"XML Scene Producer", create_xml_scene_producer, describe_xml_scene_producer);
+       dependencies.cg_registry->register_cg_producer(
+                       L"scene",
+                       { L".scene" },
+                       [](const std::wstring& filename) { return ""; },
+                       [](const spl::shared_ptr<core::frame_producer>& producer)
+                       {
+                               return spl::make_shared<core::scene::scene_cg_proxy>(producer);
+                       },
+                       [](
+                       const core::frame_producer_dependencies& dependencies,
+                       const std::wstring& filename)
+                       {
+                               return create_xml_scene_producer(dependencies, { filename });
+                       },
+                       false);
+}
+
 }}}
index ce9dcd16c2c41b26beb7a6ef966d365d9337463f..2c1f22e0824b95a8cee59f44eb0cfe5c7c6ba1e1 100644 (file)
@@ -34,6 +34,8 @@
 #include <core/monitor/monitor.h>
 #include <core/consumer/frame_consumer.h>
 #include <core/module_dependencies.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <modules/image/consumer/image_consumer.h>
 
@@ -63,64 +65,67 @@ public:
 };
 
 
-namespace caspar { namespace core {
-       namespace text {
+namespace caspar { namespace core { namespace text {
 
-               using namespace boost::filesystem;
+using namespace boost::filesystem;
 
-               std::map<std::wstring, std::wstring> fonts;
+std::map<std::wstring, std::wstring> fonts;
 
-               std::map<std::wstring, std::wstring> enumerate_fonts()
-               {
-                       std::map<std::wstring, std::wstring> result;
+std::map<std::wstring, std::wstring> enumerate_fonts()
+{
+       std::map<std::wstring, std::wstring> result;
+
+       FT_Library lib;
+       FT_Error err = FT_Init_FreeType(&lib);
+       if(err) 
+               return result;
 
-                       FT_Library lib;
-                       FT_Error err = FT_Init_FreeType(&lib);
+       auto fonts = directory_iterator(env::font_folder());
+       auto end = directory_iterator();
+       for(; fonts != end; ++fonts)
+       {
+               auto file = (*fonts);
+               if(is_regular_file(file.path()))
+               {
+                       FT_Face face;
+                       err = FT_New_Face(lib, u8(file.path().native()).c_str(), 0, &face);
                        if(err) 
-                               return result;
+                               continue;
 
-                       auto fonts = directory_iterator(env::font_folder());
-                       auto end = directory_iterator();
-                       for(; fonts != end; ++fonts)
+                       const char* fontname = FT_Get_Postscript_Name(face);    //this doesn't work for .fon fonts. Ignoring those for now
+                       if(fontname != nullptr)
                        {
-                               auto file = (*fonts);
-                               if(is_regular_file(file.path()))
-                               {
-                                       FT_Face face;
-                                       err = FT_New_Face(lib, u8(file.path().native()).c_str(), 0, &face);
-                                       if(err) 
-                                               continue;
-
-                                       const char* fontname = FT_Get_Postscript_Name(face);    //this doesn't work for .fon fonts. Ignoring those for now
-                                       if(fontname != nullptr)
-                                       {
-                                               std::string fontname_str(fontname);
-                                               result.insert(std::make_pair(u16(fontname_str), u16(file.path().native())));
-                                       }
-
-                                       FT_Done_Face(face);
-                               }
+                               std::string fontname_str(fontname);
+                               result.insert(std::make_pair(u16(fontname_str), u16(file.path().native())));
                        }
 
-                       FT_Done_FreeType(lib);
-
-                       return result;
+                       FT_Done_Face(face);
                }
+       }
 
-               void init(module_dependencies dependencies)
-               {
-                       fonts = enumerate_fonts();
-                       dependencies.producer_registry->register_producer_factory(create_text_producer);
-               }
+       FT_Done_FreeType(lib);
 
-               text_info& find_font_file(text_info& info)
-               {
-                       auto& font_name = info.font;
-                       auto it = std::find_if(fonts.begin(), fonts.end(), font_comparer(font_name));
-                       info.font_file = (it != fonts.end()) ? (*it).second : L"";
-                       return info;
-               }
-       }
+       return result;
+}
+
+void describe_text_producer(help_sink&, const help_repository&);
+spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies&, const std::vector<std::wstring>&);
+
+void init(module_dependencies dependencies)
+{
+       fonts = enumerate_fonts();
+       dependencies.producer_registry->register_producer_factory(L"Text Producer", create_text_producer, describe_text_producer);
+}
+
+text_info& find_font_file(text_info& info)
+{
+       auto& font_name = info.font;
+       auto it = std::find_if(fonts.begin(), fonts.end(), font_comparer(font_name));
+       info.font_file = (it != fonts.end()) ? (*it).second : L"";
+       return info;
+}
+
+} // namespace text
        
 
 struct text_producer::impl
@@ -314,6 +319,27 @@ spl::shared_ptr<text_producer> text_producer::create(const spl::shared_ptr<frame
 {
        return spl::make_shared<text_producer>(frame_factory, x, y, str, text_info, parent_width, parent_height, standalone);
 }
+namespace text {
+
+void describe_text_producer(help_sink& sink, const help_repository& repo)
+{
+       sink.short_description(L"A producer for rendering dynamic text.");
+       sink.syntax(L"[TEXT] [text:string] {[x:int] [y:int]} {FONT [font:string]|verdana} {SIZE [size:float]|30.0} {COLOR [color:string]|#ffffffff} {STANDALONE [standalone:0,1]|0}");
+       sink.para()
+               ->text(L"Renders dynamic text using fonts found under the ")->code(L"fonts")->text(L" folder. ")
+               ->text(L"Parameters:");
+       sink.definitions()
+               ->item(L"text", L"The text to display. Can be changed later via CALL as well.")
+               ->item(L"x", L"The x position of the text.")
+               ->item(L"y", L"The y position of the text.")
+               ->item(L"font", L"The name of the font (not the actual filename, but the font name).")
+               ->item(L"size", L"The point size.")
+               ->item(L"color", L"The color as an ARGB hex value.")
+               ->item(L"standalone", L"Whether to normalize coordinates or not.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 [TEXT] \"John Doe\" 0 0 FONT ArialMT SIZE 30 COLOR #1b698d STANDALONE 1");
+       sink.example(L">> CALL 1-10 \"Jane Doe\"", L"for modifying the text while playing.");
+}
 
 spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {
@@ -347,4 +373,4 @@ spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_depend
                        standalone);
 }
 
-}}
+}}}
index e9df2e7b04d94ee67bd0e2e787cf2995ecaf0c86..065eebf2fe1beeee3d5227488e3cee885ac40617 100644 (file)
@@ -68,7 +68,4 @@ private:
        spl::unique_ptr<impl> impl_;
 };
 
-spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
-
-
 }}
index 1f0424ebc2bf09bcefc96d113d2ef16495b61b1a..8a2aa54ef93af39918cc3b42a2a8728306bfb561 100644 (file)
@@ -84,7 +84,7 @@ void init(core::module_dependencies dependencies)
 {
        dependencies.consumer_registry->register_consumer_factory(create_consumer);
        dependencies.consumer_registry->register_preconfigured_consumer_factory(L"decklink", create_preconfigured_consumer);
-       dependencies.producer_registry->register_producer_factory(create_producer);
+       dependencies.producer_registry->register_producer_factory(L"Decklink Producer", create_producer, describe_producer);
        dependencies.system_info_provider_repo->register_system_info_provider([](boost::property_tree::wptree& info)
        {
                info.add(L"system.decklink.version", version());
index 0134b7448b7cc1115b2cd84e775c5b23fa33cdbb..e74b1bfa3ed1f558dfd43ea45336aa62fe639bef 100644 (file)
@@ -44,6 +44,8 @@
 #include <core/producer/frame_producer.h>
 #include <core/monitor/monitor.h>
 #include <core/mixer/audio/audio_mixer.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <tbb/concurrent_queue.h>
 
@@ -363,6 +365,22 @@ public:
        }
 };
 
+void describe_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Allows video sources to be input from BlackMagic Design cards.");
+       sink.syntax(L"DECKLINK [device:int],DEVICE [device:int] {FILTER [filter:string]} {LENGTH [length:int]} {FORMAT [format:string]}");
+       sink.para()->text(L"Allows video sources to be input from BlackMagic Design cards. Parameters:");
+       sink.definitions()
+               ->item(L"device", L"The decklink device to stream the input from. See the Blackmagic control panel for the order of devices in your system.")
+               ->item(L"filter", L"If specified, sets an FFmpeg video filter to use.")
+               ->item(L"length", L"Optionally specify a limit on how many frames to produce.")
+               ->item(L"format", L"Specifies what video format to expect on the incoming SDI/HDMI signal. If not specified the video format of the channel is assumed.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 DECKLINK DEVICE 2", L"Play using decklink device 2 expecting the video signal to have the same video format as the channel.");
+       sink.example(L">> PLAY 1-10 DECKLINK DEVICE 2 FORMAT PAL FILTER yadif=1:-1", L"Play using decklink device 2 expecting the video signal to be in PAL and deinterlace it.");
+       sink.example(L">> PLAY 1-10 DECKLINK DEVICE 2 LENGTH 1000", L"Play using decklink device 2 but only produce 1000 frames.");
+}
+
 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {
        if(params.empty() || !boost::iequals(params.at(0), "decklink"))
index 7e206df86c34729d9c6141a2f51556a5b489e955..00d7b796d1f68375da90ae9eb9dbd3a83caf71e0 100644 (file)
@@ -27,7 +27,8 @@
 #include <vector>
 
 namespace caspar { namespace decklink {
-       
+
+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);
 
 }}
index ad0c2e9ba436bf8da166a0829b79c1b57ecfcfff..195c0239ec609788a0caff402061484306d11346 100644 (file)
@@ -251,7 +251,7 @@ void init(core::module_dependencies dependencies)
        dependencies.consumer_registry->register_consumer_factory(create_streaming_consumer);
        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(create_producer);
+       dependencies.producer_registry->register_producer_factory(L"FFmpeg Producer", create_producer, describe_producer);
        
        dependencies.media_info_repo->register_extractor(
                        [](const std::wstring& file, const std::wstring& extension, core::media_info& info) -> bool
index 872905ea34d0347c5c2d507a2c77126f2cee2da5..bf517df6384ae8e30138eeeefad0daeffa12a2ab 100644 (file)
@@ -37,6 +37,7 @@
 #include <common/diagnostics/graph.h>
 #include <common/future.h>
 #include <common/timer.h>
+#include <common/assert.h>
 
 #include <core/video_format.h>
 #include <core/producer/frame_producer.h>
 #include <core/frame/draw_frame.h>
 #include <core/frame/frame_transform.h>
 #include <core/monitor/monitor.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <boost/algorithm/string.hpp>
-#include <common/assert.h>
 #include <boost/filesystem.hpp>
 #include <boost/property_tree/ptree.hpp>
 #include <boost/regex.hpp>
@@ -397,6 +399,33 @@ public:
        }
 };
 
+void describe_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"A producer for playing media files supported by FFmpeg.");
+       sink.syntax(L"[clip:string] {[loop:LOOP]} {START,SEEK [start:int]} {LENGTH [start:int]} {FILTER [filter:string]}");
+       sink.para()
+               ->text(L"The FFmpeg Producer can play all media that FFmpeg can play, which includes many ")
+               ->text(L"QuickTime video codec such as Animation, PNG, PhotoJPEG, MotionJPEG, as well as ")
+               ->text(L"H.264, FLV, WMV and several audio codecs as well as uncompressed audio.");
+       sink.definitions()
+               ->item(L"clip", L"The file without the file extension to play. It should reside under the media folder.")
+               ->item(L"loop", L"Will cause the media file to loop between start and start + length")
+               ->item(L"start", L"Optionally sets the start frame. 0 by default. If loop is specified this will be the frame where it starts over again.")
+               ->item(L"length", L"Optionally sets the length of the clip. If not specified the clip will be played to the end. If loop is specified the file will jump to start position once this number of frames has been played.")
+               ->item(L"filter", L"If specified, will be used as an FFmpeg video filter.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 folder/clip", L"to play all frames in a clip and stop at the last frame.");
+       sink.example(L">> PLAY 1-10 folder/clip LOOP", L"to loop a clip between the first frame and the last frame.");
+       sink.example(L">> PLAY 1-10 folder/clip LOOP START 10", L"to loop a clip between frame 10 and the last frame.");
+       sink.example(L">> PLAY 1-10 folder/clip LOOP START 10 LENGTH 50", L"to loop a clip between frame 10 and frame 60.");
+       sink.example(L">> PLAY 1-10 folder/clip START 10 LENGTH 50", L"to play frames 10-60 in a clip and stop.");
+       sink.example(L">> PLAY 1-10 folder/clip FILTER yadif=1,-1", L"to deinterlace the video.");
+       sink.para()->text(L"The FFmpeg producer also supports changing some of the settings via CALL:");
+       sink.example(L">> CALL 1-10 LOOP 1");
+       sink.example(L">> CALL 1-10 START 10");
+       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));
index b73752699cd1df23d513e3c3dcf74dd8eb52cf62..4359b2c90504d60b5f7b6771d1ee378fb7ac43ad 100644 (file)
@@ -30,6 +30,7 @@
 
 namespace caspar { namespace ffmpeg {
 
+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);
 
 }}
\ No newline at end of file
index ced2f370a2416268278557ea5190e1ac865b387d..aa3e80121b1d8b2cfe859c15ec43363bbe77b299 100644 (file)
@@ -35,6 +35,8 @@
 #include <core/system_info_provider.h>
 #include <core/frame/frame_factory.h>
 #include <core/video_format.h>
+#include <core/help/help_sink.h>
+#include <core/help/help_repository.h>
 
 #include <boost/property_tree/ptree.hpp>
 #include <boost/noncopyable.hpp>
@@ -159,6 +161,16 @@ public:
        }
 };
 
+void describe_ct_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Plays compressed flash templates (.ct files).");
+       sink.syntax(L"[ct_file:string]");
+       sink.para()->text(L"Plays compressed flash templates (.ct files). The file should reside under the media folder.");
+       sink.para()->text(L"A ct file is a zip file containing a flash template (.ft), an XML file with template data and media files.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 folder/ct_file");
+}
+
 spl::shared_ptr<core::frame_producer> create_ct_producer(
                const core::frame_producer_dependencies& dependencies,
                const std::vector<std::wstring>& params)
@@ -175,8 +187,8 @@ spl::shared_ptr<core::frame_producer> create_ct_producer(
 
 void init(core::module_dependencies dependencies)
 {
-       dependencies.producer_registry->register_producer_factory(create_ct_producer);
-       dependencies.producer_registry->register_producer_factory(create_swf_producer);
+       dependencies.producer_registry->register_producer_factory(L"Flash Producer (.ct)", create_ct_producer, describe_ct_producer);
+       dependencies.producer_registry->register_producer_factory(L"Flash Producer (.swf)", create_swf_producer, describe_swf_producer);
        dependencies.media_info_repo->register_extractor([](const std::wstring& file, const std::wstring& extension, core::media_info& info)
        {
                if (extension != L".CT" && extension != L".SWF")
index 2be1f177cdf359cfb853216823dad2fde20585a4..cde1bcd958ca08a464254bf0b1079e22fff84e2a 100644 (file)
@@ -39,6 +39,8 @@
 #include <core/frame/pixel_format.h>
 #include <core/producer/frame_producer.h>
 #include <core/monitor/monitor.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <common/env.h>
 #include <common/executor.h>
@@ -512,6 +514,15 @@ spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer
        return create_destroy_proxy(spl::make_shared<flash_producer>(dependencies.frame_factory, dependencies.format_desc, filename, template_host.width, template_host.height));
 }
 
+void describe_swf_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Plays flash files (.swf files).");
+       sink.syntax(L"[swf_file:string]");
+       sink.para()->text(L"Plays flash files (.swf files). The file should reside under the media folder.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 folder/swf_file");
+}
+
 spl::shared_ptr<core::frame_producer> create_swf_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {
        auto filename = env::media_folder() + L"\\" + params.at(0) + L".swf";
index 6f0a9448e8a953067fc3ca8b97a4cee5d2feca0a..f4396b0ec4f7de831d5c16023d3e25699b515bd2 100644 (file)
@@ -31,6 +31,7 @@
 namespace caspar { namespace flash {
 
 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
+void describe_swf_producer(core::help_sink& sink, const core::help_repository& repo);
 spl::shared_ptr<core::frame_producer> create_swf_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
 
 std::wstring find_template(const std::wstring& templateName);
index bffdfb14221607dd1315a6d27e11593dc8388a33..ad45dda213ee121ba6b33baf359d8bed2ebd1fa9 100644 (file)
@@ -286,7 +286,7 @@ bool intercept_command_line(int argc, char** argv)
 
 void init(core::module_dependencies dependencies)
 {
-       dependencies.producer_registry->register_producer_factory(html::create_producer);
+       dependencies.producer_registry->register_producer_factory(L"HTML Producer", html::create_producer, html::describe_producer);
        
        CefMainArgs main_args;
        g_cef_executor.reset(new executor(L"cef"));
index d9ef6c302f26124cd2edc8f08e6128595934940f..fba02d353891cbbc37c94991015150e80e2f8528 100644 (file)
@@ -31,6 +31,8 @@
 #include <core/frame/frame.h>
 #include <core/frame/pixel_format.h>
 #include <core/frame/geometry.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <common/assert.h>
 #include <common/env.h>
 #pragma comment (lib, "libcef.lib")
 #pragma comment (lib, "libcef_dll_wrapper.lib")
 
-namespace caspar {
-       namespace html {
+namespace caspar { namespace html {
                
-               class html_client
-                       : public CefClient
-                       , public CefRenderHandler
-                       , public CefLifeSpanHandler
-                       , public CefLoadHandler
+class html_client
+       : public CefClient
+       , public CefRenderHandler
+       , public CefLifeSpanHandler
+       , public CefLoadHandler
+{
+       std::wstring                                                    url_;
+       spl::shared_ptr<diagnostics::graph>             graph_;
+       boost::timer                                                    tick_timer_;
+       boost::timer                                                    frame_timer_;
+       boost::timer                                                    paint_timer_;
+
+       spl::shared_ptr<core::frame_factory>    frame_factory_;
+       core::video_format_desc                                 format_desc_;
+       tbb::concurrent_queue<std::wstring>             javascript_before_load_;
+       tbb::atomic<bool>                                               loaded_;
+       tbb::atomic<bool>                                               removed_;
+       tbb::atomic<bool>                                               animation_frame_requested_;
+       std::queue<core::draw_frame>                    frames_;
+       mutable boost::mutex                                    frames_mutex_;
+
+       core::draw_frame                                                last_frame_;
+       core::draw_frame                                                last_progressive_frame_;
+       mutable boost::mutex                                    last_frame_mutex_;
+
+       CefRefPtr<CefBrowser>                                   browser_;
+
+       executor                                                                executor_;
+
+public:
+
+       html_client(
+                       spl::shared_ptr<core::frame_factory> frame_factory,
+                       const core::video_format_desc& format_desc,
+                       const std::wstring& url)
+               : url_(url)
+               , frame_factory_(std::move(frame_factory))
+               , format_desc_(format_desc)
+               , last_frame_(core::draw_frame::empty())
+               , last_progressive_frame_(core::draw_frame::empty())
+               , executor_(L"html_producer")
+       {
+               graph_->set_color("browser-tick-time", diagnostics::color(0.1f, 1.0f, 0.1f));
+               graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
+               graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.9f));
+               graph_->set_text(print());
+               diagnostics::register_graph(graph_);
+
+               loaded_ = false;
+               removed_ = false;
+               animation_frame_requested_ = false;
+               executor_.begin_invoke([&]{ update(); });
+       }
+
+       core::draw_frame receive()
+       {
+               auto frame = last_frame();
+               executor_.begin_invoke([&]{ update(); });
+               return frame;
+       }
+
+       core::draw_frame last_frame() const
+       {
+               return lock(last_frame_mutex_, [&]
                {
-                       std::wstring                                                    url_;
-                       spl::shared_ptr<diagnostics::graph>             graph_;
-                       boost::timer                                                    tick_timer_;
-                       boost::timer                                                    frame_timer_;
-                       boost::timer                                                    paint_timer_;
-
-                       spl::shared_ptr<core::frame_factory>    frame_factory_;
-                       core::video_format_desc                                 format_desc_;
-                       tbb::concurrent_queue<std::wstring>             javascript_before_load_;
-                       tbb::atomic<bool>                                               loaded_;
-                       tbb::atomic<bool>                                               removed_;
-                       tbb::atomic<bool>                                               animation_frame_requested_;
-                       std::queue<core::draw_frame>                    frames_;
-                       mutable boost::mutex                                    frames_mutex_;
-
-                       core::draw_frame                                                last_frame_;
-                       core::draw_frame                                                last_progressive_frame_;
-                       mutable boost::mutex                                    last_frame_mutex_;
-
-                       CefRefPtr<CefBrowser>                                   browser_;
-
-                       executor                                                                executor_;
-
-               public:
-
-                       html_client(
-                                       spl::shared_ptr<core::frame_factory> frame_factory,
-                                       const core::video_format_desc& format_desc,
-                                       const std::wstring& url)
-                               : url_(url)
-                               , frame_factory_(std::move(frame_factory))
-                               , format_desc_(format_desc)
-                               , last_frame_(core::draw_frame::empty())
-                               , last_progressive_frame_(core::draw_frame::empty())
-                               , executor_(L"html_producer")
-                       {
-                               graph_->set_color("browser-tick-time", diagnostics::color(0.1f, 1.0f, 0.1f));
-                               graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
-                               graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.9f));
-                               graph_->set_text(print());
-                               diagnostics::register_graph(graph_);
-
-                               loaded_ = false;
-                               removed_ = false;
-                               animation_frame_requested_ = false;
-                               executor_.begin_invoke([&]{ update(); });
-                       }
+                       return last_frame_;
+               });
+       }
 
-                       core::draw_frame receive()
-                       {
-                               auto frame = last_frame();
-                               executor_.begin_invoke([&]{ update(); });
-                               return frame;
-                       }
+       void execute_javascript(const std::wstring& javascript)
+       {
+               if (!loaded_)
+               {
+                       javascript_before_load_.push(javascript);
+               }
+               else
+               {
+                       execute_queued_javascript();
+                       do_execute_javascript(javascript);
+               }
+       }
 
-                       core::draw_frame last_frame() const
-                       {
-                               return lock(last_frame_mutex_, [&]
-                               {
-                                       return last_frame_;
-                               });
-                       }
+       CefRefPtr<CefBrowserHost> get_browser_host()
+       {
+               return browser_->GetHost();
+       }
 
-                       void execute_javascript(const std::wstring& javascript)
-                       {
-                               if (!loaded_)
-                               {
-                                       javascript_before_load_.push(javascript);
-                               }
-                               else
-                               {
-                                       execute_queued_javascript();
-                                       do_execute_javascript(javascript);
-                               }
-                       }
+       void close()
+       {
+               if (!animation_frame_requested_)
+                       CASPAR_LOG(warning) << print()
+                                       << " window.requestAnimationFrame() never called. "
+                                       << "Animations might have been laggy";
 
-                       CefRefPtr<CefBrowserHost> get_browser_host()
+               html::invoke([=]
+               {
+                       if (browser_ != nullptr)
                        {
-                               return browser_->GetHost();
+                               browser_->GetHost()->CloseBrowser(true);
                        }
+               });
+       }
 
-                       void close()
-                       {
-                               if (!animation_frame_requested_)
-                                       CASPAR_LOG(warning) << print()
-                                                       << " window.requestAnimationFrame() never called. "
-                                                       << "Animations might have been laggy";
+       void remove()
+       {
+               close();
+               removed_ = true;
+       }
 
-                               html::invoke([=]
-                               {
-                                       if (browser_ != nullptr)
-                                       {
-                                               browser_->GetHost()->CloseBrowser(true);
-                                       }
-                               });
-                       }
+       bool is_removed() const
+       {
+               return removed_;
+       }
 
-                       void remove()
-                       {
-                               close();
-                               removed_ = true;
-                       }
+private:
 
-                       bool is_removed() const
-                       {
-                               return removed_;
-                       }
+       bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
+       {
+               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
 
-               private:
+               rect = CefRect(0, 0, format_desc_.square_width, format_desc_.square_height);
+               return true;
+       }
 
-                       bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
-                       {
-                               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+       void OnPaint(
+                       CefRefPtr<CefBrowser> browser,
+                       PaintElementType type,
+                       const RectList &dirtyRects,
+                       const void *buffer,
+                       int width,
+                       int height)
+       {
+               graph_->set_value("browser-tick-time", paint_timer_.elapsed()
+                               * format_desc_.fps
+                               * format_desc_.field_count
+                               * 0.5);
+               paint_timer_.restart();
+               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+
+               boost::timer copy_timer;
+               core::pixel_format_desc pixel_desc;
+                       pixel_desc.format = core::pixel_format::bgra;
+                       pixel_desc.planes.push_back(
+                               core::pixel_format_desc::plane(width, height, 4));
+               auto frame = frame_factory_->create_frame(this, pixel_desc);
+               A_memcpy(frame.image_data().begin(), buffer, width * height * 4);
+
+               lock(frames_mutex_, [&]
+               {
+                       frames_.push(core::draw_frame(std::move(frame)));
 
-                               rect = CefRect(0, 0, format_desc_.square_width, format_desc_.square_height);
-                               return true;
-                       }
+                       size_t max_in_queue = format_desc_.field_count;
 
-                       void OnPaint(
-                                       CefRefPtr<CefBrowser> browser,
-                                       PaintElementType type,
-                                       const RectList &dirtyRects,
-                                       const void *buffer,
-                                       int width,
-                                       int height)
+                       while (frames_.size() > max_in_queue)
                        {
-                               graph_->set_value("browser-tick-time", paint_timer_.elapsed()
-                                               * format_desc_.fps
-                                               * format_desc_.field_count
-                                               * 0.5);
-                               paint_timer_.restart();
-                               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
-
-                               boost::timer copy_timer;
-                               core::pixel_format_desc pixel_desc;
-                                       pixel_desc.format = core::pixel_format::bgra;
-                                       pixel_desc.planes.push_back(
-                                               core::pixel_format_desc::plane(width, height, 4));
-                               auto frame = frame_factory_->create_frame(this, pixel_desc);
-                               A_memcpy(frame.image_data().begin(), buffer, width * height * 4);
-
-                               lock(frames_mutex_, [&]
-                               {
-                                       frames_.push(core::draw_frame(std::move(frame)));
+                               frames_.pop();
+                               graph_->set_tag("dropped-frame");
+                       }
+               });
+               graph_->set_value("copy-time", copy_timer.elapsed()
+                               * format_desc_.fps
+                               * format_desc_.field_count
+                               * 0.5);
+       }
 
-                                       size_t max_in_queue = format_desc_.field_count;
+       void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
+       {
+               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
 
-                                       while (frames_.size() > max_in_queue)
-                                       {
-                                               frames_.pop();
-                                               graph_->set_tag("dropped-frame");
-                                       }
-                               });
-                               graph_->set_value("copy-time", copy_timer.elapsed()
-                                               * format_desc_.fps
-                                               * format_desc_.field_count
-                                               * 0.5);
-                       }
+               browser_ = browser;
+       }
 
-                       void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
-                       {
-                               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+       void OnBeforeClose(CefRefPtr<CefBrowser> browser) override
+       {
+               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
 
-                               browser_ = browser;
-                       }
+               browser_ = nullptr;
+       }
 
-                       void OnBeforeClose(CefRefPtr<CefBrowser> browser) override
-                       {
-                               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+       bool DoClose(CefRefPtr<CefBrowser> browser) override
+       {
+               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
 
-                               browser_ = nullptr;
-                       }
+               return false;
+       }
 
-                       bool DoClose(CefRefPtr<CefBrowser> browser) override
-                       {
-                               CASPAR_ASSERT(CefCurrentlyOn(TID_UI));
+       CefRefPtr<CefRenderHandler> GetRenderHandler() override
+       {
+               return this;
+       }
 
-                               return false;
-                       }
+       CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override
+       {
+               return this;
+       }
 
-                       CefRefPtr<CefRenderHandler> GetRenderHandler() override
-                       {
-                               return this;
-                       }
+       CefRefPtr<CefLoadHandler> GetLoadHandler() override {
+               return this;
+       }
 
-                       CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override
-                       {
-                               return this;
-                       }
+       void OnLoadEnd(
+                       CefRefPtr<CefBrowser> browser,
+                       CefRefPtr<CefFrame> frame,
+                       int httpStatusCode) override
+       {
+               loaded_ = true;
+               execute_queued_javascript();
+       }
 
-                       CefRefPtr<CefLoadHandler> GetLoadHandler() override {
-                               return this;
-                       }
+       bool OnProcessMessageReceived(
+                       CefRefPtr<CefBrowser> browser,
+                       CefProcessId source_process,
+                       CefRefPtr<CefProcessMessage> message) override
+       {
+               auto name = message->GetName().ToString();
 
-                       void OnLoadEnd(
-                                       CefRefPtr<CefBrowser> browser,
-                                       CefRefPtr<CefFrame> frame,
-                                       int httpStatusCode) override
-                       {
-                               loaded_ = true;
-                               execute_queued_javascript();
-                       }
+               if (name == ANIMATION_FRAME_REQUESTED_MESSAGE_NAME)
+               {
+                       CASPAR_LOG(trace)
+                                       << print() << L" Requested animation frame";
+                       animation_frame_requested_ = true;
 
-                       bool OnProcessMessageReceived(
-                                       CefRefPtr<CefBrowser> browser,
-                                       CefProcessId source_process,
-                                       CefRefPtr<CefProcessMessage> message) override
-                       {
-                               auto name = message->GetName().ToString();
+                       return true;
+               }
+               else if (name == REMOVE_MESSAGE_NAME)
+               {
+                       remove();
 
-                               if (name == ANIMATION_FRAME_REQUESTED_MESSAGE_NAME)
-                               {
-                                       CASPAR_LOG(trace)
-                                                       << print() << L" Requested animation frame";
-                                       animation_frame_requested_ = true;
+                       return true;
+               }
+               else if (name == LOG_MESSAGE_NAME)
+               {
+                       auto args = message->GetArgumentList();
+                       auto severity =
+                               static_cast<boost::log::trivial::severity_level>(args->GetInt(0));
+                       auto msg = args->GetString(1).ToWString();
+
+                       BOOST_LOG_STREAM_WITH_PARAMS(
+                                       log::logger::get(),
+                                       (boost::log::keywords::severity = severity))
+                               << print() << L" [renderer_process] " << msg;
+               }
 
-                                       return true;
-                               }
-                               else if (name == REMOVE_MESSAGE_NAME)
-                               {
-                                       remove();
+               return false;
+       }
 
-                                       return true;
-                               }
-                               else if (name == LOG_MESSAGE_NAME)
-                               {
-                                       auto args = message->GetArgumentList();
-                                       auto severity =
-                                               static_cast<boost::log::trivial::severity_level>(args->GetInt(0));
-                                       auto msg = args->GetString(1).ToWString();
-
-                                       BOOST_LOG_STREAM_WITH_PARAMS(
-                                                       log::logger::get(),
-                                                       (boost::log::keywords::severity = severity))
-                                               << print() << L" [renderer_process] " << msg;
-                               }
-
-                               return false;
-                       }
+       void invoke_requested_animation_frames()
+       {
+               if (browser_)
+                       browser_->SendProcessMessage(
+                                       CefProcessId::PID_RENDERER,
+                                       CefProcessMessage::Create(TICK_MESSAGE_NAME));
+               graph_->set_value("tick-time", tick_timer_.elapsed()
+                               * format_desc_.fps
+                               * format_desc_.field_count
+                               * 0.5);
+               tick_timer_.restart();
+       }
 
-                       void invoke_requested_animation_frames()
+       bool try_pop(core::draw_frame& result)
+       {
+               return lock(frames_mutex_, [&]() -> bool
+               {
+                       if (!frames_.empty())
                        {
-                               if (browser_)
-                                       browser_->SendProcessMessage(
-                                                       CefProcessId::PID_RENDERER,
-                                                       CefProcessMessage::Create(TICK_MESSAGE_NAME));
-                               graph_->set_value("tick-time", tick_timer_.elapsed()
-                                               * format_desc_.fps
-                                               * format_desc_.field_count
-                                               * 0.5);
-                               tick_timer_.restart();
+                               result = std::move(frames_.front());
+                               frames_.pop();
+
+                               return true;
                        }
 
-                       bool try_pop(core::draw_frame& result)
-                       {
-                               return lock(frames_mutex_, [&]() -> bool
-                               {
-                                       if (!frames_.empty())
-                                       {
-                                               result = std::move(frames_.front());
-                                               frames_.pop();
+                       return false;
+               });
+       }
 
-                                               return true;
-                                       }
+       core::draw_frame pop()
+       {
+               core::draw_frame frame;
 
-                                       return false;
-                               });
-                       }
+               if (!try_pop(frame))
+               {
+                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " No frame in buffer"));
+               }
 
-                       core::draw_frame pop()
-                       {
-                               core::draw_frame frame;
+               return frame;
+       }
 
-                               if (!try_pop(frame))
-                               {
-                                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " No frame in buffer"));
-                               }
+       void update()
+       {
+               invoke_requested_animation_frames();
 
-                               return frame;
-                       }
+               prec_timer timer;
+               timer.tick(0.0);
 
-                       void update()
+               auto num_frames = lock(frames_mutex_, [&]
+               {
+                       return frames_.size();
+               });
+
+               if (num_frames >= format_desc_.field_count)
+               {
+                       if (format_desc_.field_mode != core::field_mode::progressive)
                        {
+                               auto frame1 = pop();
+
+                               executor_.yield(caspar::task_priority::lowest_priority);
+                               timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
                                invoke_requested_animation_frames();
 
-                               prec_timer timer;
-                               timer.tick(0.0);
+                               auto frame2 = pop();
 
-                               auto num_frames = lock(frames_mutex_, [&]
+                               lock(last_frame_mutex_, [&]
                                {
-                                       return frames_.size();
+                                       last_progressive_frame_ = frame2;
+                                       last_frame_ = core::draw_frame::interlace(frame1, frame2, format_desc_.field_mode);
                                });
-
-                               if (num_frames >= format_desc_.field_count)
-                               {
-                                       if (format_desc_.field_mode != core::field_mode::progressive)
-                                       {
-                                               auto frame1 = pop();
-
-                                               executor_.yield(caspar::task_priority::lowest_priority);
-                                               timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
-                                               invoke_requested_animation_frames();
-
-                                               auto frame2 = pop();
-
-                                               lock(last_frame_mutex_, [&]
-                                               {
-                                                       last_progressive_frame_ = frame2;
-                                                       last_frame_ = core::draw_frame::interlace(frame1, frame2, format_desc_.field_mode);
-                                               });
-                                       }
-                                       else
-                                       {
-                                               auto frame = pop();
-
-                                               lock(last_frame_mutex_, [&]
-                                               {
-                                                       last_frame_ = frame;
-                                               });
-                                       }
-                               }
-                               else if (num_frames == 1) // Interlaced but only one frame
-                               {                         // available. Probably the last frame
-                                                         // of some animation sequence.
-                                       auto frame = pop();
-
-                                       lock(last_frame_mutex_, [&]
-                                       {
-                                               last_progressive_frame_ = frame;
-                                               last_frame_ = frame;
-                                       });
-
-                                       timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
-                                       invoke_requested_animation_frames();
-                               }
-                               else
-                               {
-                                       graph_->set_tag("late-frame");
-
-                                       if (format_desc_.field_mode != core::field_mode::progressive)
-                                       {
-                                               lock(last_frame_mutex_, [&]
-                                               {
-                                                       last_frame_ = last_progressive_frame_;
-                                               });
-
-                                               timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
-                                               invoke_requested_animation_frames();
-                                       }
-                               }
                        }
-
-                       void do_execute_javascript(const std::wstring& javascript)
+                       else
                        {
-                               html::begin_invoke([=]
+                               auto frame = pop();
+
+                               lock(last_frame_mutex_, [&]
                                {
-                                       if (browser_ != nullptr)
-                                               browser_->GetMainFrame()->ExecuteJavaScript(u8(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
+                                       last_frame_ = frame;
                                });
                        }
+               }
+               else if (num_frames == 1) // Interlaced but only one frame
+               {                         // available. Probably the last frame
+                                                   // of some animation sequence.
+                       auto frame = pop();
 
-                       void execute_queued_javascript()
+                       lock(last_frame_mutex_, [&]
                        {
-                               std::wstring javascript;
+                               last_progressive_frame_ = frame;
+                               last_frame_ = frame;
+                       });
 
-                               while (javascript_before_load_.try_pop(javascript))
-                                       do_execute_javascript(javascript);
-                       }
+                       timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
+                       invoke_requested_animation_frames();
+               }
+               else
+               {
+                       graph_->set_tag("late-frame");
 
-                       std::wstring print() const
+                       if (format_desc_.field_mode != core::field_mode::progressive)
                        {
-                               return L"html[" + url_ + L"]";
-                       }
+                               lock(last_frame_mutex_, [&]
+                               {
+                                       last_frame_ = last_progressive_frame_;
+                               });
 
-                       IMPLEMENT_REFCOUNTING(html_client);
-               };
+                               timer.tick(1.0 / (format_desc_.fps * format_desc_.field_count));
+                               invoke_requested_animation_frames();
+                       }
+               }
+       }
 
-               class html_producer
-                       : public core::frame_producer_base
+       void do_execute_javascript(const std::wstring& javascript)
+       {
+               html::begin_invoke([=]
                {
-                       core::monitor::subject  monitor_subject_;
-                       const std::wstring              url_;
-                       core::constraints               constraints_;
-
-                       CefRefPtr<html_client>  client_;
-
-               public:
-                       html_producer(
-                               const spl::shared_ptr<core::frame_factory>& frame_factory,
-                               const core::video_format_desc& format_desc,
-                               const std::wstring& url)
-                               : url_(url)
-                       {
-                               constraints_.width.set(format_desc.square_width);
-                               constraints_.height.set(format_desc.square_height);
+                       if (browser_ != nullptr)
+                               browser_->GetMainFrame()->ExecuteJavaScript(u8(javascript).c_str(), browser_->GetMainFrame()->GetURL(), 0);
+               });
+       }
 
-                               html::invoke([&]
-                               {
-                                       client_ = new html_client(frame_factory, format_desc, url_);
+       void execute_queued_javascript()
+       {
+               std::wstring javascript;
 
-                                       CefWindowInfo window_info;
+               while (javascript_before_load_.try_pop(javascript))
+                       do_execute_javascript(javascript);
+       }
 
-                                       window_info.SetTransparentPainting(true);
-                                       window_info.SetAsOffScreen(nullptr);
-                                       //window_info.SetAsWindowless(nullptr, true);
-                                       
-                                       CefBrowserSettings browser_settings;
-                                       CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
-                               });
-                       }
+       std::wstring print() const
+       {
+               return L"html[" + url_ + L"]";
+       }
 
-                       ~html_producer()
-                       {
-                               if (client_)
-                                       client_->close();
-                       }
+       IMPLEMENT_REFCOUNTING(html_client);
+};
+
+class html_producer
+       : public core::frame_producer_base
+{
+       core::monitor::subject  monitor_subject_;
+       const std::wstring              url_;
+       core::constraints               constraints_;
+
+       CefRefPtr<html_client>  client_;
+
+public:
+       html_producer(
+               const spl::shared_ptr<core::frame_factory>& frame_factory,
+               const core::video_format_desc& format_desc,
+               const std::wstring& url)
+               : url_(url)
+       {
+               constraints_.width.set(format_desc.square_width);
+               constraints_.height.set(format_desc.square_height);
+
+               html::invoke([&]
+               {
+                       client_ = new html_client(frame_factory, format_desc, url_);
 
-                       // frame_producer
+                       CefWindowInfo window_info;
 
-                       std::wstring name() const override
-                       {
-                               return L"html";
-                       }
+                       window_info.SetTransparentPainting(true);
+                       window_info.SetAsOffScreen(nullptr);
+                       //window_info.SetAsWindowless(nullptr, true);
+                                       
+                       CefBrowserSettings browser_settings;
+                       CefBrowserHost::CreateBrowser(window_info, client_.get(), url, browser_settings, nullptr);
+               });
+       }
 
-                       void on_interaction(const core::interaction_event::ptr& event) override
-                       {
-                               if (core::is<core::mouse_move_event>(event))
-                               {
-                                       auto move = core::as<core::mouse_move_event>(event);
-                                       int x = static_cast<int>(move->x * constraints_.width.get());
-                                       int y = static_cast<int>(move->y * constraints_.height.get());
-
-                                       CefMouseEvent e;
-                                       e.x = x;
-                                       e.y = y;
-                                       client_->get_browser_host()->SendMouseMoveEvent(e, false);
-                               }
-                               else if (core::is<core::mouse_button_event>(event))
-                               {
-                                       auto button = core::as<core::mouse_button_event>(event);
-                                       int x = static_cast<int>(button->x * constraints_.width.get());
-                                       int y = static_cast<int>(button->y * constraints_.height.get());
-
-                                       CefMouseEvent e;
-                                       e.x = x;
-                                       e.y = y;
-                                       client_->get_browser_host()->SendMouseClickEvent(
-                                                       e,
-                                                       static_cast<CefBrowserHost::MouseButtonType>(button->button),
-                                                       !button->pressed,
-                                                       1);
-                               }
-                               else if (core::is<core::mouse_wheel_event>(event))
-                               {
-                                       auto wheel = core::as<core::mouse_wheel_event>(event);
-                                       int x = static_cast<int>(wheel->x * constraints_.width.get());
-                                       int y = static_cast<int>(wheel->y * constraints_.height.get());
-
-                                       CefMouseEvent e;
-                                       e.x = x;
-                                       e.y = y;
-                                       static const int WHEEL_TICKS_AMPLIFICATION = 40;
-                                       client_->get_browser_host()->SendMouseWheelEvent(
-                                                       e,
-                                                       0,                                               // delta_x
-                                                       wheel->ticks_delta * WHEEL_TICKS_AMPLIFICATION); // delta_y
-                               }
-                       }
+       ~html_producer()
+       {
+               if (client_)
+                       client_->close();
+       }
 
-                       bool collides(double x, double y) const override
-                       {
-                               return true;
-                       }
+       // frame_producer
 
-                       core::draw_frame receive_impl() override
-                       {
-                               if (client_)
-                               {
-                                       if (client_->is_removed())
-                                       {
-                                               client_ = nullptr;
-                                               return core::draw_frame::empty();
-                                       }
+       std::wstring name() const override
+       {
+               return L"html";
+       }
+
+       void on_interaction(const core::interaction_event::ptr& event) override
+       {
+               if (core::is<core::mouse_move_event>(event))
+               {
+                       auto move = core::as<core::mouse_move_event>(event);
+                       int x = static_cast<int>(move->x * constraints_.width.get());
+                       int y = static_cast<int>(move->y * constraints_.height.get());
+
+                       CefMouseEvent e;
+                       e.x = x;
+                       e.y = y;
+                       client_->get_browser_host()->SendMouseMoveEvent(e, false);
+               }
+               else if (core::is<core::mouse_button_event>(event))
+               {
+                       auto button = core::as<core::mouse_button_event>(event);
+                       int x = static_cast<int>(button->x * constraints_.width.get());
+                       int y = static_cast<int>(button->y * constraints_.height.get());
+
+                       CefMouseEvent e;
+                       e.x = x;
+                       e.y = y;
+                       client_->get_browser_host()->SendMouseClickEvent(
+                                       e,
+                                       static_cast<CefBrowserHost::MouseButtonType>(button->button),
+                                       !button->pressed,
+                                       1);
+               }
+               else if (core::is<core::mouse_wheel_event>(event))
+               {
+                       auto wheel = core::as<core::mouse_wheel_event>(event);
+                       int x = static_cast<int>(wheel->x * constraints_.width.get());
+                       int y = static_cast<int>(wheel->y * constraints_.height.get());
+
+                       CefMouseEvent e;
+                       e.x = x;
+                       e.y = y;
+                       static const int WHEEL_TICKS_AMPLIFICATION = 40;
+                       client_->get_browser_host()->SendMouseWheelEvent(
+                                       e,
+                                       0,                                               // delta_x
+                                       wheel->ticks_delta * WHEEL_TICKS_AMPLIFICATION); // delta_y
+               }
+       }
 
-                                       return client_->receive();
-                               }
+       bool collides(double x, double y) const override
+       {
+               return true;
+       }
 
+       core::draw_frame receive_impl() override
+       {
+               if (client_)
+               {
+                       if (client_->is_removed())
+                       {
+                               client_ = nullptr;
                                return core::draw_frame::empty();
                        }
 
-                       std::future<std::wstring> call(const std::vector<std::wstring>& params) override
-                       {
-                               if (!client_)
-                                       return make_ready_future(std::wstring(L""));
+                       return client_->receive();
+               }
 
-                               auto javascript = params.at(0);
+               return core::draw_frame::empty();
+       }
 
-                               client_->execute_javascript(javascript);
+       std::future<std::wstring> call(const std::vector<std::wstring>& params) override
+       {
+               if (!client_)
+                       return make_ready_future(std::wstring(L""));
 
-                               return make_ready_future(std::wstring(L""));
-                       }
+               auto javascript = params.at(0);
 
-                       std::wstring print() const override
-                       {
-                               return L"html[" + url_ + L"]";
-                       }
+               client_->execute_javascript(javascript);
 
-                       boost::property_tree::wptree info() const override
-                       {
-                               boost::property_tree::wptree info;
-                               info.add(L"type", L"html-producer");
-                               return info;
-                       }
+               return make_ready_future(std::wstring(L""));
+       }
 
-                       core::constraints& pixel_constraints() override
-                       {
-                               return constraints_;
-                       }
+       std::wstring print() const override
+       {
+               return L"html[" + url_ + L"]";
+       }
 
-                       core::monitor::subject& monitor_output()
-                       {
-                               return monitor_subject_;
-                       }
-               };
+       boost::property_tree::wptree info() const override
+       {
+               boost::property_tree::wptree info;
+               info.add(L"type", L"html-producer");
+               return info;
+       }
 
-               spl::shared_ptr<core::frame_producer> create_producer(
-                               const core::frame_producer_dependencies& dependencies,
-                               const std::vector<std::wstring>& params)
-               {
-                       const auto filename = env::template_folder() + params.at(0) + L".html";
-                       const auto found_filename = find_case_insensitive(filename);
+       core::constraints& pixel_constraints() override
+       {
+               return constraints_;
+       }
+
+       core::monitor::subject& monitor_output()
+       {
+               return monitor_subject_;
+       }
+};
+
+void describe_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Renders a web page in real time.");
+       sink.syntax(L"{[html_filename:string]},{[HTML] [url:string]}");
+       sink.para()->text(L"Embeds an actual web browser and renders the content in realtime.");
+       sink.para()
+               ->text(L"HTML content can either be stored locally under the ")->code(L"templates")
+               ->text(L" folder or fetched directly via an URL. If a .html file is found with the name ")
+               ->code(L"html_filename")->text(L" under the ")->code(L"templates")->text(L" folder it will be rendered. If the ")
+               ->code(L"[HTML] url")->text(L" syntax is used instead, the URL will be loaded.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 [HTML] http://www.casparcg.com");
+       sink.example(L">> PLAY 1-10 folder/html_file");
+}
+
+spl::shared_ptr<core::frame_producer> create_producer(
+               const core::frame_producer_dependencies& dependencies,
+               const std::vector<std::wstring>& params)
+{
+       const auto filename = env::template_folder() + params.at(0) + L".html";
+       const auto found_filename = find_case_insensitive(filename);
 
-                       if (!found_filename && !boost::iequals(params.at(0), L"[HTML]"))
-                               return core::frame_producer::empty();
+       if (!found_filename && !boost::iequals(params.at(0), L"[HTML]"))
+               return core::frame_producer::empty();
 
-                       const auto url = found_filename 
-                               ? L"file://" + *found_filename
-                               : params.at(1);
+       const auto url = found_filename 
+               ? L"file://" + *found_filename
+               : params.at(1);
                
-                       if (!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
-                               return core::frame_producer::empty();
+       if (!boost::algorithm::contains(url, ".") || boost::algorithm::ends_with(url, "_A") || boost::algorithm::ends_with(url, "_ALPHA"))
+               return core::frame_producer::empty();
 
-                       return core::create_destroy_proxy(spl::make_shared<html_producer>(
-                                       dependencies.frame_factory,
-                                       dependencies.format_desc,
-                                       url));
-               }
-       }
+       return core::create_destroy_proxy(spl::make_shared<html_producer>(
+                       dependencies.frame_factory,
+                       dependencies.format_desc,
+                       url));
 }
+
+}}
index e753aaa574b1b544162f9115e9ac1e75c395e867..97e6f8561da21894763bb8d98099edd8205ab0c2 100644 (file)
@@ -30,6 +30,7 @@
 
 namespace caspar { namespace html {
 
+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);
index 04adae10203639b5fbe73260c9b5b0490d2f3572..b0d7438a65975f28758ea79dfabd9378ab5f8166 100644 (file)
@@ -47,8 +47,8 @@ std::wstring version()
 void init(core::module_dependencies dependencies)
 {
        FreeImage_Initialise();
-       dependencies.producer_registry->register_producer_factory(create_scroll_producer);
-       dependencies.producer_registry->register_producer_factory(create_producer);
+       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.consumer_registry->register_consumer_factory(create_consumer);
        dependencies.media_info_repo->register_extractor([](const std::wstring& file, const std::wstring& extension, core::media_info& info)
index 7dae24ce57ab2a96c2745e173f8f1cc4076137f8..2f8b34306537cf9e765ea15c14df18bc29abffdc 100644 (file)
@@ -32,6 +32,8 @@
 #include <core/frame/frame_factory.h>
 #include <core/frame/pixel_format.h>
 #include <core/monitor/monitor.h>
+#include <core/help/help_sink.h>
+#include <core/help/help_repository.h>
 
 #include <common/env.h>
 #include <common/log.h>
@@ -169,6 +171,16 @@ public:
        }
 };
 
+void describe_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Loads a still image.");
+       sink.syntax(L"{[image_file:string]},{[PNG_BASE64] [encoded:string]}");
+       sink.para()->text(L"Loads a still image, either from disk or via a base64 encoded image submitted via AMCP.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 image_file", L"Plays an image from the media folder.");
+       sink.example(L">> PLAY 1-10 [PNG_BASE64] data...", L"Plays a PNG image transferred as a base64 encoded string.");
+}
+
 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {
        static const auto extensions = {
@@ -264,4 +276,5 @@ spl::shared_ptr<core::frame_producer> create_thumbnail_producer(const core::fram
 {
        return caspar::image::create_producer(dependencies, params);
 }
+
 }}
index 19753a21a192ba7e0a3d2e25332b0f04adea0bdf..d60de6315f59eb616fb0620624ea0105d92892ec 100644 (file)
@@ -28,6 +28,7 @@
 
 namespace caspar { namespace image {
 
+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);
 
index 35ed1aec29cdce75bc9904ea48af21ac212e1989..f489bb73041587327a401d7fb67263df39f81e6d 100644 (file)
@@ -34,6 +34,8 @@
 #include <core/frame/frame_transform.h>
 #include <core/frame/pixel_format.h>
 #include <core/monitor/monitor.h>
+#include <core/help/help_sink.h>
+#include <core/help/help_repository.h>
 
 #include <common/env.h>
 #include <common/log.h>
@@ -394,6 +396,25 @@ struct image_scroll_producer : public core::frame_producer_base
        }
 };
 
+void describe_scroll_producer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"Scrolls an image either horizontally or vertically.");
+       sink.syntax(L"[image_file:string] SPEED [speed:float] {BLUR [blur_px:int]} {[premultiply:PREMULTIPLY]} {[progressive:PROGRESSIVE]}");
+       sink.para()
+               ->text(L"Scrolls an image either horizontally or vertically. ")
+               ->text(L"It is the image dimensions that decide if it will be a vertical scroll or a horizontal scroll. ")
+               ->text(L"A horizontal scroll will be selected if the image height is exactly the same as the video format height. ")
+               ->text(L"A vertical scroll will be selected if the image width is exactly the same as the video format width.");
+       sink.definitions()
+               ->item(L"image_file", L"The image without extension. The file has to have either the same width or the same height as the video format.")
+               ->item(L"speed", L"A positive or negative float defining how many pixels to move the image each frame.")
+               ->item(L"blur_px", L"If specified, will do a directional blur in the scrolling direction by the given number of pixels.")
+               ->item(L"premultiply", L"If the image is in straight alpha, use this option to make it display correctly in CasparCG.")
+               ->item(L"progressive", L"When an interlaced video format is used, by default the image is moved every field. This can be overridden by specifying this option, causing the image to only move on full frames.");
+       sink.para()->text(L"If ")->code(L"SPEED [speed]")->text(L" is ommitted, the ordinary ")->see(L"Image Producer")->text(L" will be used instead.");
+       sink.example(L">> PLAY 1-10 cred_1280 SPEED 8 BLUR 2", L"Given that cred_1280 is a as wide as the video mode, this will create a rolling end credits with a little bit of blur and a speed of 8 pixels per frame.");
+}
+
 spl::shared_ptr<core::frame_producer> create_scroll_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {
        static const auto extensions = {
index a0f94676509ca9400e2e259437eeb9a5ea4eedfc..7b51819178f68b1d7421b0dc5a2112b00963aab1 100644 (file)
@@ -28,6 +28,7 @@
 
 namespace caspar { namespace image {
 
+void describe_scroll_producer(core::help_sink& sink, const core::help_repository& repo);
 spl::shared_ptr<core::frame_producer> create_scroll_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
 
 }}
\ No newline at end of file
index 148e53c307518fbafd0f1ad1662a891842b77396..0ed3742f79f9754eb1cde7eac201cf2edde1c215 100644 (file)
@@ -34,6 +34,8 @@
 #include <core/producer/scene/hotswap_producer.h>
 #include <core/producer/media_info/media_info.h>
 #include <core/frame/draw_frame.h>
+#include <core/help/help_repository.h>
+#include <core/help/help_sink.h>
 
 #include <common/env.h>
 #include <common/memory.h>
@@ -455,19 +457,6 @@ spl::shared_ptr<core::frame_producer> create_psd_scene_producer(const core::fram
 
 void init(core::module_dependencies dependencies)
 {
-       dependencies.producer_registry->register_producer_factory(create_psd_scene_producer);
-       dependencies.media_info_repo->register_extractor(
-                       [](const std::wstring& file, const std::wstring& upper_case_extension, core::media_info& info)
-                       {
-                               if (upper_case_extension == L".PSD")
-                               {
-                                       info.clip_type = L"STILL";
-
-                                       return true;
-                               }
-
-                               return false;
-                       });
        dependencies.cg_registry->register_cg_producer(
                        L"scene",
                        { L".psd" },
index e4095aa1b7fa8085cac39dafa34d3bcae1767159..4241bc7dd19ac0ac075fa1575f2acc33f7cdaf83 100644 (file)
 
 #include <core/producer/frame_producer.h>
 #include <core/video_channel.h>
+#include <core/help/help_sink.h>
+#include <core/help/help_repository.h>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/range/algorithm/find_if.hpp>
 
 namespace caspar { namespace reroute {
-               
+
+void describe_producer(core::help_sink& sink, const core::help_repository& repository)
+{
+       sink.short_description(L"Reroutes a complete channel or a layer to another layer.");
+       sink.syntax(L"route://[source_channel:int]{-[source_layer:int]}");
+       sink.para()->text(L"Reroutes the composited video of a channel or the untransformed video of a layer .");
+       sink.para()
+               ->text(L"If ")->code(L"source_layer")->text(L" is specified, only the video of the source layer is rerouted. ")
+               ->text(L"If on the other hand only ")->code(L"source_channel")->text(L" is specified, the video of the complete channel is rerouted.");
+       sink.para()->text(L"Examples:");
+       sink.example(L">> PLAY 1-10 route://1-11", L"Play the contents of layer 1-11 on layer 1-10 as well.");
+       sink.example(L">> PLAY 1-10 route://2", L"Play the composited contents of channel 2 on layer 1-10 as well.");
+       sink.example(
+               L">> MIXER 1-10 FILL 0.02 0.01 0.9 0.9\n"
+               L">> PLAY 1-10 route://1\n"
+               L">> PLAY 1-9 AMB LOOP", L"Play the composited contents of channel 1 on layer 1-10. Since the source and destination channel is the same, an \"infinity\" effect is created.");
+       sink.para()->text(L"Always expect a few frames delay on the routed-to layer.");
+}
+
 spl::shared_ptr<core::frame_producer> create_producer(
                const core::frame_producer_dependencies& dependencies,
                const std::vector<std::wstring>& params)
index dc258335a28bf63a29b22ae71f8b6e85db97e7d0..37cc6470ce0295a25cfa9e1188600e7779903ca1 100644 (file)
@@ -29,7 +29,8 @@
 #include <string>
 
 namespace caspar { namespace reroute {
-       
+
+void describe_producer(core::help_sink& sink, const core::help_repository& repository);
 spl::shared_ptr<core::frame_producer> create_producer(
                const core::frame_producer_dependencies& dependencies,
                const std::vector<std::wstring>& params);
index 89967769bbe42703375bdfb695da385fe880651b..501bd0ebfcdd6735b499640972e5c8a46aff87d1 100644 (file)
@@ -30,7 +30,7 @@ namespace caspar { namespace reroute {
 
 void init(core::module_dependencies dependencies)
 {
-       dependencies.producer_registry->register_producer_factory(reroute::create_producer);
+       dependencies.producer_registry->register_producer_factory(L"Reroute Producer", reroute::create_producer, reroute::describe_producer);
 }
 
 }}
index 0828e8754ff799fd45546582b96fd71e8898aac0..a602b16a3c35f4c86ef8360f8b8bd3211bd34205 100644 (file)
@@ -2402,147 +2402,168 @@ std::wstring diag_command(command_context& ctx)
        return L"202 DIAG OK\r\n";
 }
 
-void help_describer(core::help_sink& sink, const core::help_repository& repository)
-{
-       sink.short_description(L"Show online help.");
-       sink.syntax(LR"(HELP {[command:string]})");
-       sink.para()->text(LR"(Shows online help for a specific command or a list of all commands.)");
-       sink.example(L">> HELP", L"Shows a list of commands.");
-       sink.example(L">> HELP PLAY", L"Shows a detailed description of the PLAY command.");
-}
+static const int WIDTH = 80;
 
-std::wstring help_command(command_context& ctx)
+struct max_width_sink : public core::help_sink
 {
-       static const int WIDTH = 80;
-       struct max_width_sink : public core::help_sink
-       {
-               std::size_t max_width = 0;
+       std::size_t max_width = 0;
 
-               void begin_item(const std::wstring& name) override
-               {
-                       max_width = std::max(name.length(), max_width);
-               };
-       };
-
-       struct short_description_sink : public core::help_sink
+       void begin_item(const std::wstring& name) override
        {
-               std::size_t width;
-               std::wstringstream& out;
+               max_width = std::max(name.length(), max_width);
+       };
+};
 
-               short_description_sink(std::size_t width, std::wstringstream& out) : width(width), out(out) { }
+struct short_description_sink : public core::help_sink
+{
+       std::size_t width;
+       std::wstringstream& out;
 
-               void begin_item(const std::wstring& name) override
-               {
-                       out << std::left << std::setw(width + 1) << name;
-               };
+       short_description_sink(std::size_t width, std::wstringstream& out) : width(width), out(out) { }
 
-               void short_description(const std::wstring& short_description) override
-               {
-                       out << short_description << L"\r\n";
-               };
+       void begin_item(const std::wstring& name) override
+       {
+               out << std::left << std::setw(width + 1) << name;
        };
 
-       struct simple_paragraph_builder : core::paragraph_builder
+       void short_description(const std::wstring& short_description) override
        {
-               std::wostringstream out;
-               std::wstringstream& commit_to;
-
-               simple_paragraph_builder(std::wstringstream& out) : commit_to(out) { }
-               ~simple_paragraph_builder()
-               {
-                       commit_to << core::wordwrap(out.str(), WIDTH) << L"\n";
-               }
-               spl::shared_ptr<paragraph_builder> text(std::wstring text) override
-               {
-                       out << std::move(text);
-                       return shared_from_this();
-               }
-               spl::shared_ptr<paragraph_builder> code(std::wstring txt) override { return text(std::move(txt)); }
-               spl::shared_ptr<paragraph_builder> see(std::wstring item) override { return text(std::move(item)); }
-               spl::shared_ptr<paragraph_builder> url(std::wstring url, std::wstring name)  override { return text(std::move(url)); }
+               out << short_description << L"\r\n";
        };
+};
+
+struct simple_paragraph_builder : core::paragraph_builder
+{
+       std::wostringstream out;
+       std::wstringstream& commit_to;
 
-       struct simple_definition_list_builder : core::definition_list_builder
+       simple_paragraph_builder(std::wstringstream& out) : commit_to(out) { }
+       ~simple_paragraph_builder()
+       {
+               commit_to << core::wordwrap(out.str(), WIDTH) << L"\n";
+       }
+       spl::shared_ptr<paragraph_builder> text(std::wstring text) override
        {
-               std::wstringstream& out;
+               out << std::move(text);
+               return shared_from_this();
+       }
+       spl::shared_ptr<paragraph_builder> code(std::wstring txt) override { return text(std::move(txt)); }
+       spl::shared_ptr<paragraph_builder> see(std::wstring item) override { return text(std::move(item)); }
+       spl::shared_ptr<paragraph_builder> url(std::wstring url, std::wstring name)  override { return text(std::move(url)); }
+};
 
-               simple_definition_list_builder(std::wstringstream& out) : out(out) { }
-               ~simple_definition_list_builder()
-               {
-                       out << L"\n";
-               }
+struct simple_definition_list_builder : core::definition_list_builder
+{
+       std::wstringstream& out;
 
-               spl::shared_ptr<definition_list_builder> item(std::wstring term, std::wstring description) override
-               {
-                       out << core::indent(core::wordwrap(term, WIDTH - 2), L"  ");
-                       out << core::indent(core::wordwrap(description, WIDTH - 4), L"    ");
-                       return shared_from_this();
-               }
-       };
+       simple_definition_list_builder(std::wstringstream& out) : out(out) { }
+       ~simple_definition_list_builder()
+       {
+               out << L"\n";
+       }
 
-       struct long_description_sink : public core::help_sink
+       spl::shared_ptr<definition_list_builder> item(std::wstring term, std::wstring description) override
        {
-               std::wstringstream& out;
+               out << core::indent(core::wordwrap(term, WIDTH - 2), L"  ");
+               out << core::indent(core::wordwrap(description, WIDTH - 4), L"    ");
+               return shared_from_this();
+       }
+};
 
-               long_description_sink(std::wstringstream& out) : out(out) { }
+struct long_description_sink : public core::help_sink
+{
+       std::wstringstream& out;
 
-               void syntax(const std::wstring& syntax) override
-               {
-                       out << L"Syntax:\n";
-                       out << core::indent(core::wordwrap(syntax, WIDTH - 2), L"  ") << L"\n";
-               };
+       long_description_sink(std::wstringstream& out) : out(out) { }
 
-               spl::shared_ptr<core::paragraph_builder> para() override
-               {
-                       return spl::make_shared<simple_paragraph_builder>(out);
-               }
+       void syntax(const std::wstring& syntax) override
+       {
+               out << L"Syntax:\n";
+               out << core::indent(core::wordwrap(syntax, WIDTH - 2), L"  ") << L"\n";
+       };
 
-               spl::shared_ptr<core::definition_list_builder> definitions() override
-               {
-                       return spl::make_shared<simple_definition_list_builder>(out);
-               }
+       spl::shared_ptr<core::paragraph_builder> para() override
+       {
+               return spl::make_shared<simple_paragraph_builder>(out);
+       }
 
-               void example(const std::wstring& code, const std::wstring& caption = L"") override
-               {
-                       out << core::indent(core::wordwrap(code, WIDTH - 2), L"  ");
+       spl::shared_ptr<core::definition_list_builder> definitions() override
+       {
+               return spl::make_shared<simple_definition_list_builder>(out);
+       }
 
-                       if (!caption.empty())
-                               out << core::indent(core::wordwrap(L"..." + caption, WIDTH - 2), L"  ");
+       void example(const std::wstring& code, const std::wstring& caption) override
+       {
+               out << core::indent(core::wordwrap(code, WIDTH - 2), L"  ");
 
-                       out << L"\n";
-               }
-       private:
-               void begin_item(const std::wstring& name) override
-               {
-                       out << name << L"\n\n";
-               };
+               if (!caption.empty())
+                       out << core::indent(core::wordwrap(L"..." + caption, WIDTH - 2), L"  ");
+
+               out << L"\n";
+       }
+private:
+       void begin_item(const std::wstring& name) override
+       {
+               out << name << L"\n\n";
        };
+};
 
+std::wstring create_help_list(const std::wstring& help_command, const command_context& ctx, std::set<std::wstring> tags)
+{
        std::wstringstream result;
+       result << L"200 " << help_command << L" OK\r\n";
+       max_width_sink width;
+       ctx.help_repo->help(tags, width);
+       short_description_sink sink(width.max_width, result);
+       sink.width = width.max_width;
+       ctx.help_repo->help(tags, sink);
+       result << L"\r\n";
+       return result.str();
+}
+
+std::wstring create_help_details(const std::wstring& help_command, const command_context& ctx, std::set<std::wstring> tags)
+{
+       std::wstringstream result;
+       result << L"201 " << help_command << L" OK\r\n";
+       auto joined = boost::join(ctx.parameters, L" ");
+       long_description_sink sink(result);
+       ctx.help_repo->help(tags, joined, sink);
+       result << L"\r\n";
+       return result.str();
+}
+
+void help_describer(core::help_sink& sink, const core::help_repository& repository)
+{
+       sink.short_description(L"Show online help for AMCP commands.");
+       sink.syntax(LR"(HELP {[command:string]})");
+       sink.para()->text(LR"(Shows online help for a specific command or a list of all commands.)");
+       sink.example(L">> HELP", L"Shows a list of commands.");
+       sink.example(L">> HELP PLAY", L"Shows a detailed description of the PLAY command.");
+}
 
+std::wstring help_command(command_context& ctx)
+{
        if (ctx.parameters.size() == 0)
-       {
-               result << L"200 HELP OK\r\n";
-               max_width_sink width;
-               ctx.help_repo->help({ L"AMCP" }, width);
-               short_description_sink sink(width.max_width, result);
-               sink.width = width.max_width;
-               ctx.help_repo->help({ L"AMCP" }, sink);
-               result << L"\r\n";
-       }
+               return create_help_list(L"HELP", ctx, { L"AMCP" });
        else
-       {
-               result << L"201 HELP OK\r\n";
-               auto command = boost::to_upper_copy(
-                       ctx.parameters.size() == 2
-                                       ? ctx.parameters.at(0) + L" " + ctx.parameters.at(1)
-                                       : ctx.parameters.at(0));
-               long_description_sink sink(result);
-               ctx.help_repo->help({ L"AMCP" }, command, sink);
-               result << L"\r\n";
-       }
+               return create_help_details(L"HELP", ctx, { L"AMCP" });
+}
 
-       return result.str();
+void help_producer_describer(core::help_sink& sink, const core::help_repository& repository)
+{
+       sink.short_description(L"Show online help for producers.");
+       sink.syntax(LR"(HELP PRODUCER {[producer:string]})");
+       sink.para()->text(LR"(Shows online help for a specific producer or a list of all producers.)");
+       sink.example(L">> HELP PRODUCER", L"Shows a list of producers.");
+       sink.example(L">> HELP PRODUCER FFmpeg Producer", L"Shows a detailed description of the FFmpeg Producer.");
+}
+
+std::wstring help_producer_command(command_context& ctx)
+{
+       if (ctx.parameters.size() == 0)
+               return create_help_list(L"HELP PRODUCER", ctx, { L"producer" });
+       else
+               return create_help_details(L"HELP PRODUCER", ctx, { L"producer" });
 }
 
 void bye_describer(core::help_sink& sink, const core::help_repository& repo)
@@ -2642,132 +2663,6 @@ std::wstring lock_command(command_context& ctx)
        CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Unknown LOCK command " + command));
 }
 
-/*bool LockCommand::DoExecute()
-{
-       try
-       {
-               auto it = parameters().begin();
-
-               std::shared_ptr<caspar::IO::lock_container> lock;
-               try
-               {
-                       int channel_index = boost::lexical_cast<int>(*it) - 1;
-                       lock = channels().at(channel_index).lock;
-               }
-               catch(const boost::bad_lexical_cast&) {}
-               catch(...)
-               {
-                       SetReplyString(L"401 LOCK ERROR\r\n");
-                       return false;
-               }
-
-               if(lock)
-                       ++it;
-
-               if(it == parameters().end())    //too few parameters
-               {
-                       SetReplyString(L"402 LOCK ERROR\r\n");
-                       return false;
-               }
-
-               std::wstring command = boost::to_upper_copy(*it);
-               if(command == L"ACQUIRE")
-               {
-                       ++it;
-                       if(it == parameters().end())    //too few parameters
-                       {
-                               SetReplyString(L"402 LOCK ACQUIRE ERROR\r\n");
-                               return false;
-                       }
-                       std::wstring lock_phrase = (*it);
-
-                       //TODO: read options
-
-                       if(lock)
-                       {
-                               //just lock one channel
-                               if(!lock->try_lock(lock_phrase, client()))
-                               {
-                                       SetReplyString(L"503 LOCK ACQUIRE FAILED\r\n");
-                                       return false;
-                               }
-                       }
-                       else
-                       {
-                               //TODO: lock all channels
-                               CASPAR_THROW_EXCEPTION(not_implemented());
-                       }
-                       SetReplyString(L"202 LOCK ACQUIRE OK\r\n");
-
-               }
-               else if(command == L"RELEASE")
-               {
-                       if(lock)
-                       {
-                               lock->release_lock(client());
-                       }
-                       else
-                       {
-                               //TODO: release all channels
-                               CASPAR_THROW_EXCEPTION(not_implemented());
-                       }
-                       SetReplyString(L"202 LOCK RELEASE OK\r\n");
-               }
-               else if(command == L"CLEAR")
-               {
-                       std::wstring override_phrase = env::properties().get(L"configuration.lock-clear-phrase", L"");
-                       std::wstring client_override_phrase;
-                       if(!override_phrase.empty())
-                       {
-                               ++it;
-                               if(it == parameters().end())
-                               {
-                                       SetReplyString(L"402 LOCK CLEAR ERROR\r\n");
-                                       return false;
-                               }
-                               client_override_phrase = (*it);
-                       }
-
-                       if(lock)
-                       {
-                               //just clear one channel
-                               if(client_override_phrase != override_phrase)
-                               {
-                                       SetReplyString(L"503 LOCK CLEAR FAILED\r\n");
-                                       return false;
-                               }
-                               
-                               lock->clear_locks();
-                       }
-                       else
-                       {
-                               //TODO: clear all channels
-                               CASPAR_THROW_EXCEPTION(not_implemented());
-                       }
-
-                       SetReplyString(L"202 LOCK CLEAR OK\r\n");
-               }
-               else
-               {
-                       SetReplyString(L"403 LOCK ERROR\r\n");
-                       return false;
-               }
-       }
-       catch(not_implemented&)
-       {
-               SetReplyString(L"600 LOCK FAILED\r\n");
-               return false;
-       }
-       catch(...)
-       {
-               CASPAR_LOG_CURRENT_EXCEPTION();
-               SetReplyString(L"501 LOCK FAILED\r\n");
-               return false;
-       }
-
-       return true;
-}*/
-
 void register_commands(amcp_command_repository& repo)
 {
        repo.register_channel_command(  L"Basic Commands",              L"LOADBG",                                      loadbg_describer,                                       loadbg_command,                                 1);
@@ -2844,6 +2739,7 @@ void register_commands(amcp_command_repository& repo)
        repo.register_command(                  L"Query Commands",              L"KILL",                                        kill_describer,                                         kill_command,                                   0);
        repo.register_command(                  L"Query Commands",              L"RESTART",                                     restart_describer,                                      restart_command,                                0);
        repo.register_command(                  L"Query Commands",              L"HELP",                                        help_describer,                                         help_command,                                   0);
+       repo.register_command(                  L"Query Commands",              L"HELP PRODUCER",                       help_producer_describer,                        help_producer_command,                  0);
 }
 
 }      //namespace amcp
index 78fce5b03998de4f144da285410b9a6d24daeb59..cbc7ccccc46e262fa0303bb8fba063d05eae78f8 100644 (file)
@@ -28,6 +28,7 @@
 #include <core/help/help_repository.h>
 #include <core/help/help_sink.h>
 #include <core/help/util.h>
+#include <core/producer/text/text_producer.h>
 
 #include <protocol/amcp/amcp_command_repository.h>
 #include <protocol/amcp/AMCPCommandsImpl.h>
@@ -92,7 +93,7 @@ public:
 
        ~mediawiki_definition_list_builder()
        {
-               out_ << L"\n" << std::endl;
+               out_ << std::endl;
        }
 
        spl::shared_ptr<definition_list_builder> item(std::wstring term, std::wstring description) override
@@ -170,16 +171,25 @@ void generate_amcp_commands_help(const core::help_repository& help_repo)
        file.flush();
 }
 
-int main(int argc, char** argv)
+void generate_producers_help(const core::help_repository& help_repo)
 {
-       //env::configure(L"casparcg.config");
-       //log::set_log_level(L"info");
+       boost::filesystem::wofstream file(L"producers_help.wiki");
+       mediawiki_help_sink sink(file);
 
+       sink.start_section(L"Producers (Input Modules)");
+       help_repo.help({ L"producer" }, sink);
+
+       file.flush();
+}
+
+int main(int argc, char** argv)
+{
+       env::configure(L"casparcg.config");
        spl::shared_ptr<core::system_info_provider_repository> system_info_provider_repo;
        spl::shared_ptr<core::cg_producer_registry> cg_registry;
        auto media_info_repo = core::create_in_memory_media_info_repository();
        spl::shared_ptr<core::help_repository> help_repo;
-       spl::shared_ptr<core::frame_producer_registry> producer_registry;
+       auto producer_registry = spl::make_shared<core::frame_producer_registry>(help_repo);
        spl::shared_ptr<core::frame_consumer_registry> consumer_registry;
        std::promise<bool> shutdown_server_now;
        protocol::amcp::amcp_command_repository repo(
@@ -197,8 +207,10 @@ int main(int argc, char** argv)
 
        core::module_dependencies dependencies(system_info_provider_repo, cg_registry, media_info_repo, producer_registry, consumer_registry);
        initialize_modules(dependencies);
+       core::text::init(dependencies);
 
        generate_amcp_commands_help(*help_repo);
+       generate_producers_help(*help_repo);
 
        uninitialize_modules();
        
index 9033cd87347698d36d421405c6c0f9e1f3d99982..daee0a671f00ca09eeaba037e4001b0fff12f285 100644 (file)
@@ -105,6 +105,7 @@ struct server::impl : boost::noncopyable
                : accelerator_(env::properties().get(L"configuration.accelerator", L"auto"))
                , osc_client_(io_service_manager_.service())
                , media_info_repo_(create_in_memory_media_info_repository())
+               , producer_registry_(spl::make_shared<core::frame_producer_registry>(help_repo_))
                , shutdown_server_now_(shutdown_server_now)
        {
                running_ = false;