]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/muxer/frame_muxer.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / modules / ffmpeg / producer / muxer / frame_muxer.cpp
1 #include "../../StdAfx.h"\r
2 \r
3 #include "frame_muxer.h"\r
4 \r
5 #include "display_mode.h"\r
6 \r
7 #include "../filter/filter.h"\r
8 #include "../util/util.h"\r
9 \r
10 #include <core/producer/frame_producer.h>\r
11 #include <core/producer/frame/basic_frame.h>\r
12 #include <core/producer/frame/frame_factory.h>\r
13 #include <core/mixer/write_frame.h>\r
14 \r
15 #include <common/concurrency/governor.h>\r
16 #include <common/env.h>\r
17 #include <common/exception/exceptions.h>\r
18 #include <common/log/log.h>\r
19 \r
20 #include <boost/range/algorithm_ext/push_back.hpp>\r
21 \r
22 #if defined(_MSC_VER)\r
23 #pragma warning (push)\r
24 #pragma warning (disable : 4244)\r
25 #endif\r
26 extern "C" \r
27 {\r
28         #define __STDC_CONSTANT_MACROS\r
29         #define __STDC_LIMIT_MACROS\r
30         #include <libavcodec/avcodec.h>\r
31         #include <libavformat/avformat.h>\r
32 }\r
33 #if defined(_MSC_VER)\r
34 #pragma warning (pop)\r
35 #endif\r
36 \r
37 #include <agents.h>\r
38 \r
39 using namespace Concurrency;\r
40 \r
41 namespace caspar { namespace ffmpeg {\r
42         \r
43 struct frame_muxer2::implementation : public Concurrency::agent, boost::noncopyable\r
44 {               \r
45         frame_muxer2::video_source_t* video_source_;\r
46         frame_muxer2::audio_source_t* audio_source_;\r
47 \r
48         ITarget<frame_muxer2::target_element_t>&                        target_;\r
49         mutable single_assignment<display_mode::type>           display_mode_;\r
50         const double                                                                            in_fps_;\r
51         const core::video_format_desc                                           format_desc_;\r
52         const bool                                                                                      auto_transcode_;\r
53         \r
54         mutable single_assignment<safe_ptr<filter>>                     filter_;\r
55         const safe_ptr<core::frame_factory>                                     frame_factory_;\r
56                         \r
57         core::audio_buffer                                                                      audio_data_;\r
58         \r
59         std::wstring                                                                            filter_str_;\r
60 \r
61         governor                                                                                        governor_;\r
62 \r
63         tbb::atomic<bool>                                                                       is_running_;\r
64         \r
65         size_t                                                                                          video_loop_count_;\r
66         size_t                                                                                          audio_loop_count_;\r
67                 \r
68         implementation(frame_muxer2::video_source_t* video_source,\r
69                                    frame_muxer2::audio_source_t* audio_source,\r
70                                    frame_muxer2::target_t& target,\r
71                                    double in_fps, \r
72                                    const safe_ptr<core::frame_factory>& frame_factory,\r
73                                    const std::wstring& filter)\r
74                 : video_source_(video_source)\r
75                 , audio_source_(audio_source)\r
76                 , target_(target)\r
77                 , in_fps_(in_fps)\r
78                 , format_desc_(frame_factory->get_video_format_desc())\r
79                 , auto_transcode_(env::properties().get("configuration.producers.auto-transcode", false))\r
80                 , frame_factory_(frame_factory)\r
81                 , governor_(2)\r
82                 , video_loop_count_(0)\r
83                 , audio_loop_count_(0)\r
84         {               \r
85                 is_running_ = true;\r
86                 start();\r
87         }\r
88 \r
89         ~implementation()\r
90         {\r
91                 is_running_ = false;\r
92                 governor_.cancel();\r
93                 agent::wait(this);\r
94         }\r
95                                 \r
96         std::shared_ptr<core::write_frame> receive_video()\r
97         {       \r
98                 if(!video_source_)\r
99                         return make_safe<core::write_frame>(this);      \r
100 \r
101                 auto video = filter_.has_value() ? filter_.value()->poll() : nullptr;\r
102                 if(video_loop_count_ >= audio_loop_count_ && video)             \r
103                         return make_write_frame(this, make_safe_ptr(video), frame_factory_, 0);         \r
104                 \r
105                 video = receive(video_source_);\r
106                 \r
107                 if(video == flush_video())\r
108                 {\r
109                         ++video_loop_count_;\r
110                         return receive_video();\r
111                 }\r
112 \r
113                 if(video == eof_video())\r
114                         return nullptr;         \r
115 \r
116                 if(!display_mode_.has_value())\r
117                         initialize_display_mode(*video);\r
118                         \r
119                 filter_.value()->push(video);\r
120                 video.reset();\r
121 \r
122                 return receive_video();\r
123         }\r
124         \r
125         std::shared_ptr<core::audio_buffer> receive_audio()\r
126         {               \r
127                 if(!audio_source_)\r
128                         return make_safe<core::audio_buffer>(format_desc_.audio_samples_per_frame, 0);\r
129                                         \r
130                 if(audio_loop_count_ >= video_loop_count_ && audio_data_.size() >= format_desc_.audio_samples_per_frame)\r
131                 {\r
132                         auto begin = audio_data_.begin(); \r
133                         auto end   = begin + format_desc_.audio_samples_per_frame;\r
134                         auto audio = make_safe<core::audio_buffer>(begin, end);\r
135                         audio_data_.erase(begin, end);\r
136                         return audio;\r
137                 }\r
138                                 \r
139                 std::shared_ptr<core::audio_buffer> audio = receive(audio_source_);\r
140 \r
141                 if(audio == flush_audio())\r
142                 {\r
143                         CASPAR_LOG(info) << L"Truncating: " << audio_data_.size() << L" audio-samples:";\r
144                         audio_data_.clear();\r
145                         ++audio_loop_count_;\r
146                         return receive_audio();         \r
147                 }\r
148 \r
149                 if(audio == eof_audio())\r
150                         return nullptr;\r
151 \r
152                 audio_data_.insert(audio_data_.end(), audio->begin(), audio->end());    \r
153                 audio.reset();\r
154                 \r
155                 return receive_audio();\r
156         }\r
157                         \r
158         virtual void run()\r
159         {\r
160                 win32_exception::install_handler();\r
161 \r
162                 try\r
163                 {\r
164                         while(is_running_ && display())\r
165                         {       \r
166                         }                               \r
167                 }\r
168                 catch(...)\r
169                 {\r
170                         CASPAR_LOG_CURRENT_EXCEPTION();\r
171                 }\r
172                 \r
173                 send(target_, frame_muxer2::target_element_t(core::basic_frame::eof(), ticket_t()));\r
174 \r
175                 done();\r
176         }\r
177 \r
178         bool display()\r
179         {\r
180                 auto ticket = governor_.acquire();\r
181 \r
182                 auto video = receive_video();\r
183                 if(!video)\r
184                         return false;\r
185 \r
186                 auto audio = receive_audio();\r
187                 if(!audio)\r
188                         return false;\r
189                                 \r
190                 video->audio_data() = std::move(*audio);\r
191 \r
192                 switch(display_mode_.value())\r
193                 {\r
194                 case display_mode::simple:                      \r
195                 case display_mode::deinterlace:\r
196                 case display_mode::deinterlace_bob:\r
197                         {\r
198                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
199                                 \r
200                                 return true;\r
201                         }\r
202                 case display_mode::duplicate:                                   \r
203                         {                       \r
204                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
205                                                                 \r
206                                 auto video2 = make_safe<core::write_frame>(*video);     \r
207                                 auto audio2 = receive_audio();\r
208 \r
209                                 if(audio2)\r
210                                 {\r
211                                         video2->audio_data() = std::move(*audio2);\r
212                                         send(target_, frame_muxer2::target_element_t(video2, ticket));\r
213                                 }\r
214 \r
215                                 return audio2 != nullptr;\r
216                         }\r
217                 case display_mode::half:                                                \r
218                         {                                                               \r
219                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
220                                 auto video2 = receive_video(); // throw away\r
221                                 \r
222                                 return video2 != nullptr;\r
223                         }\r
224                 case display_mode::deinterlace_bob_reinterlace:\r
225                 case display_mode::interlace:                                   \r
226                         {                                       \r
227                                 std::shared_ptr<core::basic_frame> video2 = receive_video();\r
228                                 if(!video2)\r
229                                         video2 = core::basic_frame::empty();\r
230                                                                                                 \r
231                                 auto frame = core::basic_frame::interlace(make_safe_ptr(video), make_safe_ptr(video2), format_desc_.field_mode);        \r
232                                 send(target_, frame_muxer2::target_element_t(frame, ticket));\r
233                                 \r
234                                 return video2 != nullptr;\r
235                         }\r
236                 default:        \r
237                         BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));\r
238                 }\r
239         }       \r
240         \r
241         void initialize_display_mode(AVFrame& frame)\r
242         {\r
243                 auto display_mode = display_mode::invalid;\r
244 \r
245                 if(auto_transcode_)\r
246                 {\r
247                         auto mode = get_mode(frame);\r
248                         auto fps  = in_fps_;\r
249 \r
250                         if(is_deinterlacing(filter_str_))\r
251                                 mode = core::field_mode::progressive;\r
252 \r
253                         if(is_double_rate(filter_str_))\r
254                                 fps *= 2;\r
255 \r
256                         display_mode = get_display_mode(mode, fps, format_desc_.field_mode, format_desc_.fps);\r
257                         \r
258                         if(display_mode == display_mode::simple && mode != core::field_mode::progressive && format_desc_.field_mode != core::field_mode::progressive && frame.height != static_cast<int>(format_desc_.height))\r
259                                 display_mode = display_mode::deinterlace_bob_reinterlace; // The frame will most likely be scaled, we need to deinterlace->reinterlace  \r
260                                 \r
261                         if(display_mode == display_mode::deinterlace)\r
262                                 append_filter(filter_str_, L"YADIF=0:-1");\r
263                         else if(display_mode == display_mode::deinterlace_bob || display_mode == display_mode::deinterlace_bob_reinterlace)\r
264                                 append_filter(filter_str_, L"YADIF=1:-1");\r
265                 }\r
266                 else\r
267                         display_mode = display_mode::simple;\r
268 \r
269                 if(display_mode == display_mode::invalid)\r
270                 {\r
271                         CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";\r
272                         display_mode = display_mode::simple;\r
273                 }\r
274                         \r
275                 send(filter_, make_safe<filter>(filter_str_));\r
276 \r
277                 CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode);\r
278 \r
279                 send(display_mode_, display_mode);\r
280         }\r
281                                         \r
282         int64_t calc_nb_frames(int64_t nb_frames) const\r
283         {\r
284                 switch(display_mode_.value()) // Take into account transformation in run.\r
285                 {\r
286                 case display_mode::deinterlace_bob_reinterlace:\r
287                 case display_mode::interlace:   \r
288                 case display_mode::half:\r
289                         nb_frames /= 2;\r
290                         break;\r
291                 case display_mode::duplicate:\r
292                         nb_frames *= 2;\r
293                         break;\r
294                 }\r
295 \r
296                 if(is_double_rate(widen(filter_.value()->filter_str()))) // Take into account transformations in filter.\r
297                         nb_frames *= 2;\r
298 \r
299                 return nb_frames;\r
300         }\r
301 };\r
302 \r
303 frame_muxer2::frame_muxer2(video_source_t* video_source, \r
304                                                    audio_source_t* audio_source,\r
305                                                    target_t& target,\r
306                                                    double in_fps, \r
307                                                    const safe_ptr<core::frame_factory>& frame_factory,\r
308                                                    const std::wstring& filter)\r
309         : impl_(new implementation(video_source, audio_source, target, in_fps, frame_factory, filter))\r
310 {\r
311 }\r
312 \r
313 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const\r
314 {\r
315         return impl_->calc_nb_frames(nb_frames);\r
316 }\r
317 \r
318 }}