]> 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         implementation(frame_muxer2::video_source_t* video_source,\r
66                                    frame_muxer2::audio_source_t* audio_source,\r
67                                    frame_muxer2::target_t& target,\r
68                                    double in_fps, \r
69                                    const safe_ptr<core::frame_factory>& frame_factory,\r
70                                    const std::wstring& filter)\r
71                 : video_source_(video_source)\r
72                 , audio_source_(audio_source)\r
73                 , target_(target)\r
74                 , in_fps_(in_fps)\r
75                 , format_desc_(frame_factory->get_video_format_desc())\r
76                 , auto_transcode_(env::properties().get("configuration.producers.auto-transcode", false))\r
77                 , frame_factory_(frame_factory)\r
78                 , governor_(2)\r
79         {               \r
80                 is_running_ = true;\r
81                 start();\r
82         }\r
83 \r
84         ~implementation()\r
85         {\r
86                 is_running_ = false;\r
87                 governor_.cancel();\r
88                 agent::wait(this);\r
89         }\r
90                                 \r
91         std::shared_ptr<core::write_frame> receive_video()\r
92         {       \r
93                 if(!video_source_)\r
94                         return make_safe<core::write_frame>(this);      \r
95 \r
96                 auto video = filter_.has_value() ? filter_.value()->poll() : nullptr;\r
97                 if(video)               \r
98                         return make_write_frame(this, make_safe_ptr(video), frame_factory_, 0);         \r
99                 \r
100                 video = receive(video_source_);\r
101                 \r
102                 if(video == flush_video())\r
103                         return receive_video();\r
104 \r
105                 if(video == eof_video())\r
106                         return nullptr;         \r
107 \r
108                 if(!display_mode_.has_value())\r
109                         initialize_display_mode(*video);\r
110                         \r
111                 filter_.value()->push(video);\r
112                 video.reset();\r
113 \r
114                 return receive_video();\r
115         }\r
116         \r
117         std::shared_ptr<core::audio_buffer> receive_audio()\r
118         {               \r
119                 if(!audio_source_)\r
120                         return make_safe<core::audio_buffer>(format_desc_.audio_samples_per_frame, 0);\r
121                                         \r
122                 if(audio_data_.size() >= format_desc_.audio_samples_per_frame)\r
123                 {\r
124                         auto begin = audio_data_.begin(); \r
125                         auto end   = begin + format_desc_.audio_samples_per_frame;\r
126                         auto audio = make_safe<core::audio_buffer>(begin, end);\r
127                         audio_data_.erase(begin, end);\r
128                         return audio;\r
129                 }\r
130                                 \r
131                 std::shared_ptr<core::audio_buffer> audio = receive(audio_source_);\r
132 \r
133                 if(audio == flush_audio())\r
134                         return receive_audio();\r
135 \r
136                 if(audio == eof_audio())\r
137                         return nullptr;\r
138 \r
139                 audio_data_.insert(audio_data_.end(), audio->begin(), audio->end());    \r
140                 audio.reset();\r
141                 \r
142                 return receive_audio();\r
143         }\r
144                         \r
145         virtual void run()\r
146         {\r
147                 win32_exception::install_handler();\r
148 \r
149                 try\r
150                 {\r
151                         while(is_running_ && display())\r
152                         {       \r
153                         }                               \r
154                 }\r
155                 catch(...)\r
156                 {\r
157                         CASPAR_LOG_CURRENT_EXCEPTION();\r
158                 }\r
159                 \r
160                 send(target_, frame_muxer2::target_element_t(core::basic_frame::eof(), ticket_t()));\r
161 \r
162                 done();\r
163         }\r
164 \r
165         bool display()\r
166         {\r
167                 auto ticket = governor_.acquire();\r
168 \r
169                 auto video = receive_video();\r
170                 if(!video)\r
171                         return false;\r
172 \r
173                 auto audio = receive_audio();\r
174                 if(!audio)\r
175                         return false;\r
176                                 \r
177                 video->audio_data() = std::move(*audio);\r
178 \r
179                 switch(display_mode_.value())\r
180                 {\r
181                 case display_mode::simple:                      \r
182                 case display_mode::deinterlace:\r
183                 case display_mode::deinterlace_bob:\r
184                         {\r
185                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
186                                 \r
187                                 return true;\r
188                         }\r
189                 case display_mode::duplicate:                                   \r
190                         {                       \r
191                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
192                                                                 \r
193                                 auto video2 = make_safe<core::write_frame>(*video);     \r
194                                 auto audio2 = receive_audio();\r
195 \r
196                                 if(audio2)\r
197                                 {\r
198                                         video2->audio_data() = std::move(*audio2);\r
199                                         send(target_, frame_muxer2::target_element_t(video2, ticket));\r
200                                 }\r
201 \r
202                                 return audio2 != nullptr;\r
203                         }\r
204                 case display_mode::half:                                                \r
205                         {                                                               \r
206                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
207                                 auto video2 = receive_video(); // throw away\r
208                                 \r
209                                 return video2 != nullptr;\r
210                         }\r
211                 case display_mode::deinterlace_bob_reinterlace:\r
212                 case display_mode::interlace:                                   \r
213                         {                                       \r
214                                 std::shared_ptr<core::basic_frame> video2 = receive_video();\r
215                                 if(!video2)\r
216                                         video2 = core::basic_frame::empty();\r
217                                                                                                 \r
218                                 auto frame = core::basic_frame::interlace(make_safe_ptr(video), make_safe_ptr(video2), format_desc_.field_mode);        \r
219                                 send(target_, frame_muxer2::target_element_t(frame, ticket));\r
220                                 \r
221                                 return video2 != nullptr;\r
222                         }\r
223                 default:        \r
224                         BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));\r
225                 }\r
226         }       \r
227         \r
228         void initialize_display_mode(AVFrame& frame)\r
229         {\r
230                 auto display_mode = display_mode::invalid;\r
231 \r
232                 if(auto_transcode_)\r
233                 {\r
234                         auto mode = get_mode(frame);\r
235                         auto fps  = in_fps_;\r
236 \r
237                         if(is_deinterlacing(filter_str_))\r
238                                 mode = core::field_mode::progressive;\r
239 \r
240                         if(is_double_rate(filter_str_))\r
241                                 fps *= 2;\r
242 \r
243                         display_mode = get_display_mode(mode, fps, format_desc_.field_mode, format_desc_.fps);\r
244                         \r
245                         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
246                                 display_mode = display_mode::deinterlace_bob_reinterlace; // The frame will most likely be scaled, we need to deinterlace->reinterlace  \r
247                                 \r
248                         if(display_mode == display_mode::deinterlace)\r
249                                 append_filter(filter_str_, L"YADIF=0:-1");\r
250                         else if(display_mode == display_mode::deinterlace_bob || display_mode == display_mode::deinterlace_bob_reinterlace)\r
251                                 append_filter(filter_str_, L"YADIF=1:-1");\r
252                 }\r
253                 else\r
254                         display_mode = display_mode::simple;\r
255 \r
256                 if(display_mode == display_mode::invalid)\r
257                 {\r
258                         CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";\r
259                         display_mode = display_mode::simple;\r
260                 }\r
261                         \r
262                 send(filter_, make_safe<filter>(filter_str_));\r
263 \r
264                 CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode);\r
265 \r
266                 send(display_mode_, display_mode);\r
267         }\r
268                                         \r
269         int64_t calc_nb_frames(int64_t nb_frames) const\r
270         {\r
271                 switch(display_mode_.value()) // Take into account transformation in run.\r
272                 {\r
273                 case display_mode::deinterlace_bob_reinterlace:\r
274                 case display_mode::interlace:   \r
275                 case display_mode::half:\r
276                         nb_frames /= 2;\r
277                         break;\r
278                 case display_mode::duplicate:\r
279                         nb_frames *= 2;\r
280                         break;\r
281                 }\r
282 \r
283                 if(is_double_rate(widen(filter_.value()->filter_str()))) // Take into account transformations in filter.\r
284                         nb_frames *= 2;\r
285 \r
286                 return nb_frames;\r
287         }\r
288 };\r
289 \r
290 frame_muxer2::frame_muxer2(video_source_t* video_source, \r
291                                                    audio_source_t* audio_source,\r
292                                                    target_t& target,\r
293                                                    double in_fps, \r
294                                                    const safe_ptr<core::frame_factory>& frame_factory,\r
295                                                    const std::wstring& filter)\r
296         : impl_(new implementation(video_source, audio_source, target, in_fps, frame_factory, filter))\r
297 {\r
298 }\r
299 \r
300 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const\r
301 {\r
302         return impl_->calc_nb_frames(nb_frames);\r
303 }\r
304 \r
305 }}