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