]> 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                 {\r
135                         if(!audio_data_.empty())\r
136                         {\r
137                                 CASPAR_LOG(info) << L"[frame_muxer] Truncating audio: " << audio_data_.size();\r
138                                 audio_data_.clear();\r
139                         }\r
140                 }\r
141 \r
142                 if(audio == eof_audio())\r
143                         return nullptr;\r
144 \r
145                 audio_data_.insert(audio_data_.end(), audio->begin(), audio->end());    \r
146                 audio.reset();\r
147                 \r
148                 return receive_audio();\r
149         }\r
150                         \r
151         virtual void run()\r
152         {\r
153                 win32_exception::install_handler();\r
154 \r
155                 try\r
156                 {\r
157                         while(is_running_ && display())\r
158                         {       \r
159                         }                               \r
160                 }\r
161                 catch(...)\r
162                 {\r
163                         CASPAR_LOG_CURRENT_EXCEPTION();\r
164                 }\r
165                 \r
166                 send(target_, frame_muxer2::target_element_t(core::basic_frame::eof(), ticket_t()));\r
167 \r
168                 done();\r
169         }\r
170 \r
171         bool display()\r
172         {\r
173                 auto ticket = governor_.acquire();\r
174 \r
175                 auto video = receive_video();\r
176                 if(!video)\r
177                         return false;\r
178 \r
179                 auto audio = receive_audio();\r
180                 if(!audio)\r
181                         return false;\r
182                                 \r
183                 video->audio_data() = std::move(*audio);\r
184 \r
185                 switch(display_mode_.value())\r
186                 {\r
187                 case display_mode::simple:                      \r
188                 case display_mode::deinterlace:\r
189                 case display_mode::deinterlace_bob:\r
190                         {\r
191                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
192                                 \r
193                                 return true;\r
194                         }\r
195                 case display_mode::duplicate:                                   \r
196                         {                       \r
197                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
198                                                                 \r
199                                 auto video2 = make_safe<core::write_frame>(*video);     \r
200                                 auto audio2 = receive_audio();\r
201 \r
202                                 if(audio2)\r
203                                 {\r
204                                         video2->audio_data() = std::move(*audio2);\r
205                                         send(target_, frame_muxer2::target_element_t(video2, ticket));\r
206                                 }\r
207 \r
208                                 return audio2 != nullptr;\r
209                         }\r
210                 case display_mode::half:                                                \r
211                         {                                                               \r
212                                 send(target_, frame_muxer2::target_element_t(video, ticket));\r
213                                 auto video2 = receive_video(); // throw away\r
214                                 \r
215                                 return video2 != nullptr;\r
216                         }\r
217                 case display_mode::deinterlace_bob_reinterlace:\r
218                 case display_mode::interlace:                                   \r
219                         {                                       \r
220                                 std::shared_ptr<core::basic_frame> video2 = receive_video();\r
221                                 if(!video2)\r
222                                         video2 = core::basic_frame::empty();\r
223                                                                                                 \r
224                                 auto frame = core::basic_frame::interlace(make_safe_ptr(video), make_safe_ptr(video2), format_desc_.field_mode);        \r
225                                 send(target_, frame_muxer2::target_element_t(frame, ticket));\r
226                                 \r
227                                 return video2 != nullptr;\r
228                         }\r
229                 default:        \r
230                         BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));\r
231                 }\r
232         }       \r
233         \r
234         void initialize_display_mode(AVFrame& frame)\r
235         {\r
236                 auto display_mode = display_mode::invalid;\r
237 \r
238                 if(auto_transcode_)\r
239                 {\r
240                         auto mode = get_mode(frame);\r
241                         auto fps  = in_fps_;\r
242 \r
243                         if(is_deinterlacing(filter_str_))\r
244                                 mode = core::field_mode::progressive;\r
245 \r
246                         if(is_double_rate(filter_str_))\r
247                                 fps *= 2;\r
248 \r
249                         display_mode = get_display_mode(mode, fps, format_desc_.field_mode, format_desc_.fps);\r
250                         \r
251                         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
252                                 display_mode = display_mode::deinterlace_bob_reinterlace; // The frame will most likely be scaled, we need to deinterlace->reinterlace  \r
253                                 \r
254                         if(display_mode == display_mode::deinterlace)\r
255                                 append_filter(filter_str_, L"YADIF=0:-1");\r
256                         else if(display_mode == display_mode::deinterlace_bob || display_mode == display_mode::deinterlace_bob_reinterlace)\r
257                                 append_filter(filter_str_, L"YADIF=1:-1");\r
258                 }\r
259                 else\r
260                         display_mode = display_mode::simple;\r
261 \r
262                 if(display_mode == display_mode::invalid)\r
263                 {\r
264                         CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";\r
265                         display_mode = display_mode::simple;\r
266                 }\r
267                         \r
268                 send(filter_, make_safe<filter>(filter_str_));\r
269 \r
270                 CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode);\r
271 \r
272                 send(display_mode_, display_mode);\r
273         }\r
274                                         \r
275         int64_t calc_nb_frames(int64_t nb_frames) const\r
276         {\r
277                 switch(display_mode_.value()) // Take into account transformation in run.\r
278                 {\r
279                 case display_mode::deinterlace_bob_reinterlace:\r
280                 case display_mode::interlace:   \r
281                 case display_mode::half:\r
282                         nb_frames /= 2;\r
283                         break;\r
284                 case display_mode::duplicate:\r
285                         nb_frames *= 2;\r
286                         break;\r
287                 }\r
288 \r
289                 if(is_double_rate(widen(filter_.value()->filter_str()))) // Take into account transformations in filter.\r
290                         nb_frames *= 2;\r
291 \r
292                 return nb_frames;\r
293         }\r
294 };\r
295 \r
296 frame_muxer2::frame_muxer2(video_source_t* video_source, \r
297                                                    audio_source_t* audio_source,\r
298                                                    target_t& target,\r
299                                                    double in_fps, \r
300                                                    const safe_ptr<core::frame_factory>& frame_factory,\r
301                                                    const std::wstring& filter)\r
302         : impl_(new implementation(video_source, audio_source, target, in_fps, frame_factory, filter))\r
303 {\r
304 }\r
305 \r
306 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const\r
307 {\r
308         return impl_->calc_nb_frames(nb_frames);\r
309 }\r
310 \r
311 }}