]> git.sesse.net Git - casparcg/blob - core/producer/frame_producer.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / core / producer / frame_producer.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\r
6 *    CasparCG is free software: you can redistribute it and/or modify\r
7 *    it under the terms of the GNU General Public License as published by\r
8 *    the Free Software Foundation, either version 3 of the License, or\r
9 *    (at your option) any later version.\r
10 *\r
11 *    CasparCG is distributed in the hope that it will be useful,\r
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 *    GNU General Public License for more details.\r
15 \r
16 *    You should have received a copy of the GNU General Public License\r
17 *    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 */\r
20 \r
21 #include "../StdAfx.h"\r
22 \r
23 #include "frame_producer.h"\r
24 #include "frame/basic_frame.h"\r
25 #include "frame/frame_transform.h"\r
26 \r
27 #include "color/color_producer.h"\r
28 #include "separated/separated_producer.h"\r
29 \r
30 #include <common/memory/safe_ptr.h>\r
31 #include <common/exception/exceptions.h>\r
32 \r
33 #include <concrt_extras.h>\r
34 #include <concurrent_vector.h>\r
35 \r
36 namespace caspar { namespace core {\r
37         \r
38 struct destruction_context\r
39 {\r
40         std::shared_ptr<frame_producer> producer;\r
41         Concurrency::event                              event;\r
42 \r
43         destruction_context(std::shared_ptr<frame_producer>&& producer) \r
44                 : producer(producer)\r
45         {\r
46         }\r
47 };\r
48 \r
49 void __cdecl destroy_producer(LPVOID lpParam)\r
50 {\r
51         static Concurrency::critical_section mutex;\r
52         auto destruction = std::unique_ptr<destruction_context>(static_cast<destruction_context*>(lpParam));\r
53         \r
54         try\r
55         {               \r
56                 if(destruction->producer.unique())\r
57                 {\r
58                         {\r
59                                 Concurrency::critical_section::scoped_lock lock(mutex);\r
60                                 Concurrency::wait(100);\r
61                         }\r
62                         Concurrency::scoped_oversubcription_token oversubscribe;\r
63                         CASPAR_LOG(info) << "Destroying: " << destruction->producer->print();\r
64                         destruction->producer.reset();\r
65                 }\r
66                 else\r
67                         CASPAR_LOG(warning) << destruction->producer->print() << " Not destroyed asynchronously.";              \r
68         }\r
69         catch(...)\r
70         {\r
71                 CASPAR_LOG_CURRENT_EXCEPTION();\r
72         }\r
73         \r
74         destruction->event.set();\r
75 }\r
76 \r
77 void __cdecl destroy_and_wait_producer(LPVOID lpParam)\r
78 {\r
79         try\r
80         {\r
81                 auto destruction = static_cast<destruction_context*>(lpParam);\r
82                 Concurrency::CurrentScheduler::ScheduleTask(destroy_producer, lpParam);\r
83                 if(destruction->event.wait(1000) == Concurrency::COOPERATIVE_WAIT_TIMEOUT)\r
84                         CASPAR_LOG(warning) << " Potential destruction deadlock detected. Might leak resources.";\r
85         }\r
86         catch(...)\r
87         {\r
88                 CASPAR_LOG_CURRENT_EXCEPTION();\r
89         }\r
90 }\r
91 \r
92 class destroy_producer_proxy : public frame_producer\r
93 {\r
94         std::shared_ptr<frame_producer> producer_;\r
95 public:\r
96         destroy_producer_proxy(const std::shared_ptr<frame_producer>& producer) \r
97                 : producer_(producer)\r
98         {\r
99         }\r
100 \r
101         ~destroy_producer_proxy()\r
102         {               \r
103                 Concurrency::CurrentScheduler::ScheduleTask(destroy_producer, new destruction_context(std::move(producer_)));\r
104         }\r
105 \r
106         virtual safe_ptr<basic_frame>           receive(int hints)                                                                                              {return producer_->receive(hints);}\r
107         virtual safe_ptr<basic_frame>           last_frame() const                                                                                              {return producer_->last_frame();}\r
108         virtual std::wstring                            print() const                                                                                                   {return producer_->print();}\r
109         virtual void                                            param(const std::wstring& str)                                                                  {producer_->param(str);}\r
110         virtual safe_ptr<frame_producer>        get_following_producer() const                                                                  {return producer_->get_following_producer();}\r
111         virtual void                                            set_leading_producer(const safe_ptr<frame_producer>& producer)  {producer_->set_leading_producer(producer);}\r
112         virtual int64_t                                         nb_frames() const                                                                                               {return producer_->nb_frames();}\r
113 };\r
114 \r
115 class last_frame_producer : public frame_producer\r
116 {\r
117         const std::wstring                      print_;\r
118         const safe_ptr<basic_frame>     frame_;\r
119         const int64_t                           nb_frames_;\r
120 public:\r
121         last_frame_producer(const safe_ptr<frame_producer>& producer) \r
122                 : print_(producer->print())\r
123                 , frame_(producer->last_frame() != basic_frame::eof() ? producer->last_frame() : basic_frame::empty())\r
124                 , nb_frames_(producer->nb_frames())\r
125         {\r
126         }\r
127         \r
128         virtual safe_ptr<basic_frame> receive(int){return frame_;}\r
129         virtual safe_ptr<core::basic_frame> last_frame() const{return frame_;}\r
130         virtual std::wstring print() const{return L"dummy[" + print_ + L"]";}\r
131         virtual int64_t nb_frames() const {return nb_frames_;}  \r
132 };\r
133 \r
134 struct empty_frame_producer : public frame_producer\r
135 {\r
136         virtual safe_ptr<basic_frame> receive(int){return basic_frame::empty();}\r
137         virtual safe_ptr<basic_frame> last_frame() const{return basic_frame::empty();}\r
138         virtual void set_frame_factory(const safe_ptr<frame_factory>&){}\r
139         virtual int64_t nb_frames() const {return 0;}\r
140         virtual std::wstring print() const { return L"empty";}\r
141 };\r
142 \r
143 const safe_ptr<frame_producer>& frame_producer::empty() // nothrow\r
144 {\r
145         static safe_ptr<frame_producer> producer = make_safe<empty_frame_producer>();\r
146         return producer;\r
147 }       \r
148 \r
149 safe_ptr<basic_frame> receive_and_follow(safe_ptr<frame_producer>& producer, int hints)\r
150 {       \r
151         auto frame = producer->receive(hints);\r
152         if(frame == basic_frame::eof())\r
153         {\r
154                 CASPAR_LOG(info) << producer->print() << " End Of File.";\r
155                 auto following = producer->get_following_producer();\r
156                 if(following != frame_producer::empty())\r
157                 {\r
158                         following->set_leading_producer(producer);\r
159                         producer = std::move(following);\r
160                 }\r
161                 else\r
162                         producer = make_safe<last_frame_producer>(producer);\r
163 \r
164                 return receive_and_follow(producer, hints);\r
165         }\r
166         return frame;\r
167 }\r
168         \r
169 Concurrency::concurrent_vector<std::shared_ptr<producer_factory_t>> g_factories;\r
170 \r
171 void register_producer_factory(const producer_factory_t& factory)\r
172 {\r
173         g_factories.push_back(std::make_shared<producer_factory_t>(factory));\r
174 }\r
175 \r
176 safe_ptr<core::frame_producer> do_create_producer(const safe_ptr<frame_factory>& my_frame_factory, const std::vector<std::wstring>& params)\r
177 {\r
178         if(params.empty())\r
179                 BOOST_THROW_EXCEPTION(invalid_argument() << arg_name_info("params") << arg_value_info(""));\r
180         \r
181         auto producer = frame_producer::empty();\r
182         std::any_of(g_factories.begin(), g_factories.end(), [&](const std::shared_ptr<producer_factory_t>& factory) -> bool\r
183                 {\r
184                         try\r
185                         {\r
186                                 producer = (*factory)(my_frame_factory, params);\r
187                         }\r
188                         catch(...)\r
189                         {\r
190                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
191                         }\r
192                         return producer != frame_producer::empty();\r
193                 });\r
194 \r
195         if(producer == frame_producer::empty())\r
196                 producer = create_color_producer(my_frame_factory, params);\r
197         \r
198         return producer;\r
199 }\r
200 \r
201 safe_ptr<core::frame_producer> create_producer(const safe_ptr<frame_factory>& my_frame_factory, const std::vector<std::wstring>& params)\r
202 {       \r
203         auto producer = do_create_producer(my_frame_factory, params);\r
204         auto key_producer = frame_producer::empty();\r
205         \r
206         try // to find a key file.\r
207         {\r
208                 auto params_copy = params;\r
209                 if(params_copy.size() > 0)\r
210                 {\r
211                         params_copy[0] += L"_A";\r
212                         key_producer = do_create_producer(my_frame_factory, params_copy);                       \r
213                         if(key_producer == frame_producer::empty())\r
214                         {\r
215                                 params_copy[0] += L"LPHA";\r
216                                 key_producer = do_create_producer(my_frame_factory, params_copy);       \r
217                         }\r
218                 }\r
219         }\r
220         catch(...){}\r
221 \r
222         if(producer != frame_producer::empty() && key_producer != frame_producer::empty())\r
223                 producer = create_separated_producer(producer, key_producer);\r
224         \r
225         if(producer == frame_producer::empty())\r
226         {\r
227                 std::wstring str;\r
228                 BOOST_FOREACH(auto& param, params)\r
229                         str += param + L" ";\r
230                 BOOST_THROW_EXCEPTION(file_not_found() << msg_info("No match found for supplied commands. Check syntax.") << arg_value_info(narrow(str)));\r
231         }\r
232 \r
233         return make_safe<destroy_producer_proxy>(producer);\r
234 }\r
235 \r
236 }}