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