]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/frame_muxer.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / modules / ffmpeg / producer / frame_muxer.cpp
1 #include "../StdAfx.h"\r
2 \r
3 #include "frame_muxer.h"\r
4 \r
5 #include "filter/filter.h"\r
6 \r
7 #include "util.h"\r
8 \r
9 #include <core/producer/frame_producer.h>\r
10 #include <core/producer/frame/basic_frame.h>\r
11 #include <core/producer/frame/frame_transform.h>\r
12 #include <core/producer/frame/pixel_format.h>\r
13 #include <core/producer/frame/frame_factory.h>\r
14 #include <core/mixer/write_frame.h>\r
15 \r
16 #include <common/env.h>\r
17 #include <common/exception/exceptions.h>\r
18 #include <common/log/log.h>\r
19 \r
20 #if defined(_MSC_VER)\r
21 #pragma warning (push)\r
22 #pragma warning (disable : 4244)\r
23 #endif\r
24 extern "C" \r
25 {\r
26         #define __STDC_CONSTANT_MACROS\r
27         #define __STDC_LIMIT_MACROS\r
28         #include <libavcodec/avcodec.h>\r
29         #include <libavformat/avformat.h>\r
30 }\r
31 #if defined(_MSC_VER)\r
32 #pragma warning (pop)\r
33 #endif\r
34 \r
35 #include <boost/foreach.hpp>\r
36 #include <boost/range/algorithm_ext/push_back.hpp>\r
37 #include <boost/assign.hpp>\r
38 \r
39 #include <agents_extras.h>\r
40 \r
41 #include <deque>\r
42 #include <tuple>\r
43 #include <queue>\r
44 #include <vector>\r
45 \r
46 using namespace caspar::core;\r
47 \r
48 using namespace Concurrency;\r
49 \r
50 namespace caspar { namespace ffmpeg {\r
51 \r
52 struct display_mode\r
53 {\r
54         enum type\r
55         {\r
56                 simple,\r
57                 duplicate,\r
58                 half,\r
59                 interlace,\r
60                 deinterlace_bob,\r
61                 deinterlace_bob_reinterlace,\r
62                 deinterlace,\r
63                 count,\r
64                 invalid\r
65         };\r
66 \r
67         static std::wstring print(display_mode::type value)\r
68         {\r
69                 switch(value)\r
70                 {\r
71                         case simple:                                            return L"simple";\r
72                         case duplicate:                                         return L"duplicate";\r
73                         case half:                                                      return L"half";\r
74                         case interlace:                                         return L"interlace";\r
75                         case deinterlace_bob:                           return L"deinterlace_bob";\r
76                         case deinterlace_bob_reinterlace:       return L"deinterlace_bob_reinterlace";\r
77                         case deinterlace:                                       return L"deinterlace";\r
78                         default:                                                        return L"invalid";\r
79                 }\r
80         }\r
81 };\r
82 \r
83 display_mode::type get_display_mode(const core::field_mode::type in_mode, double in_fps, const core::field_mode::type out_mode, double out_fps)\r
84 {               \r
85         static const auto epsilon = 2.0;\r
86 \r
87         if(in_fps < 20.0 || in_fps > 80.0)\r
88         {\r
89                 //if(out_mode != core::field_mode::progressive && in_mode == core::field_mode::progressive)\r
90                 //      return display_mode::interlace;\r
91                 \r
92                 if(out_mode == core::field_mode::progressive && in_mode != core::field_mode::progressive)\r
93                 {\r
94                         if(in_fps < 35.0)\r
95                                 return display_mode::deinterlace;\r
96                         else\r
97                                 return display_mode::deinterlace_bob;\r
98                 }\r
99         }\r
100 \r
101         if(std::abs(in_fps - out_fps) < epsilon)\r
102         {\r
103                 if(in_mode != core::field_mode::progressive && out_mode == core::field_mode::progressive)\r
104                         return display_mode::deinterlace;\r
105                 //else if(in_mode == core::field_mode::progressive && out_mode != core::field_mode::progressive)\r
106                 //      simple(); // interlace_duplicate();\r
107                 else\r
108                         return display_mode::simple;\r
109         }\r
110         else if(std::abs(in_fps/2.0 - out_fps) < epsilon)\r
111         {\r
112                 if(in_mode != core::field_mode::progressive)\r
113                         return display_mode::invalid;\r
114 \r
115                 if(out_mode != core::field_mode::progressive)\r
116                         return display_mode::interlace;\r
117                 else\r
118                         return display_mode::half;\r
119         }\r
120         else if(std::abs(in_fps - out_fps/2.0) < epsilon)\r
121         {\r
122                 if(out_mode != core::field_mode::progressive)\r
123                         return display_mode::invalid;\r
124 \r
125                 if(in_mode != core::field_mode::progressive)\r
126                         return display_mode::deinterlace_bob;\r
127                 else\r
128                         return display_mode::duplicate;\r
129         }\r
130 \r
131         return display_mode::invalid;\r
132 }\r
133 \r
134 \r
135 struct frame_muxer2::implementation : public Concurrency::agent, boost::noncopyable\r
136 {               \r
137         ITarget<safe_ptr<core::basic_frame>>&                   target_;\r
138         display_mode::type                                                              display_mode_;\r
139         const double                                                                    in_fps_;\r
140         const video_format_desc                                                 format_desc_;\r
141         bool                                                                                    auto_transcode_;\r
142         \r
143         filter                                                                                  filter_;\r
144         const safe_ptr<core::frame_factory>                             frame_factory_;\r
145         \r
146         call<safe_ptr<AVFrame>>                                                 push_video_;\r
147         call<safe_ptr<core::audio_buffer>>                              push_audio_;\r
148         \r
149         unbounded_buffer<safe_ptr<AVFrame>>                             video_;\r
150         unbounded_buffer<safe_ptr<core::audio_buffer>>  audio_;\r
151         \r
152         core::audio_buffer                                                              audio_data_;\r
153 \r
154         Concurrency::overwrite_buffer<bool>                             is_running_;\r
155 \r
156         safe_ptr<semaphore> semaphore_;\r
157                                                         \r
158         implementation(frame_muxer2::video_source_t* video_source,\r
159                                    frame_muxer2::audio_source_t* audio_source,\r
160                                    frame_muxer2::target_t& target,\r
161                                    double in_fps, \r
162                                    const safe_ptr<core::frame_factory>& frame_factory)\r
163                 : target_(target)\r
164                 , display_mode_(display_mode::invalid)\r
165                 , in_fps_(in_fps)\r
166                 , format_desc_(frame_factory->get_video_format_desc())\r
167                 , auto_transcode_(env::properties().get("configuration.producers.auto-transcode", false))\r
168                 , frame_factory_(make_safe<core::concrt_frame_factory>(frame_factory))\r
169                 , push_video_(std::bind(&implementation::push_video, this, std::placeholders::_1))\r
170                 , push_audio_(std::bind(&implementation::push_audio, this, std::placeholders::_1))\r
171                 , semaphore_(make_safe<semaphore>(8))\r
172         {\r
173                 if(video_source)\r
174                         video_source->link_target(&push_video_);\r
175                 if(audio_source)\r
176                         audio_source->link_target(&push_audio_);\r
177                 \r
178                 start();\r
179         }\r
180 \r
181         ~implementation()\r
182         {\r
183                 send(is_running_, false);\r
184                 agent::wait(this);\r
185         }\r
186         \r
187         virtual void run()\r
188         {\r
189                 try\r
190                 {\r
191                         send(is_running_, true);\r
192                         while(is_running_.value())\r
193                         {\r
194                                 auto video = receive(video_);\r
195                                 auto audio = receive(audio_);                                                           \r
196                                 auto frame = make_safe<core::write_frame>(this);\r
197                                 \r
198                                 if(audio == eof_audio())\r
199                                 {\r
200                                         send(is_running_ , false);\r
201                                         break;\r
202                                 }\r
203 \r
204                                 if(video == eof_video())\r
205                                 {\r
206                                         send(is_running_ , false);\r
207                                         break;\r
208                                 }\r
209 \r
210                                 if(video != empty_video())\r
211                                         frame = make_write_frame(this, video, frame_factory_, 0);\r
212                                 if(audio == empty_audio())\r
213                                         audio = make_safe<core::audio_buffer>(format_desc_.audio_samples_per_frame, 0);\r
214 \r
215                                 frame->audio_data() = std::move(*audio);\r
216 \r
217                                 switch(display_mode_)\r
218                                 {\r
219                                 case display_mode::simple:                      \r
220                                 case display_mode::deinterlace:\r
221                                 case display_mode::deinterlace_bob:\r
222                                         {\r
223                                                 send(target_, safe_ptr<core::basic_frame>(frame));\r
224 \r
225                                                 break;\r
226                                         }\r
227                                 case display_mode::duplicate:                                   \r
228                                         {                                                                               \r
229                                                 send(target_, safe_ptr<core::basic_frame>(frame));\r
230                                                 send(target_, safe_ptr<core::basic_frame>(frame));\r
231 \r
232                                                 break;\r
233                                         }\r
234                                 case display_mode::half:                                                \r
235                                         {                                       \r
236                                                 receive(video_); // throw away                          \r
237                                                 send(target_, safe_ptr<core::basic_frame>(frame));\r
238 \r
239                                                 break;\r
240                                         }\r
241                                 case display_mode::deinterlace_bob_reinterlace:\r
242                                 case display_mode::interlace:                                   \r
243                                         {                                       \r
244                                                 /*auto frame = safe_ptr<core::basic_frame>(frame);\r
245                                                 auto video2 = receive(video_);  \r
246                                                 if(video2 != empty_video() && video2 != eof_video())\r
247                                                         frame = core::basic_frame::interlace(frame, safe_ptr<core::basic_frame>(video2), format_desc_.field_mode);\r
248                                                 else\r
249                                                         send(is_running_, false);\r
250 \r
251                                                 send(target_, frame);*/\r
252 \r
253                                                 break;\r
254                                         }\r
255                                 default:        \r
256                                         BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));\r
257                                 }\r
258                         }\r
259                 }\r
260                 catch(...)\r
261                 {\r
262                         CASPAR_LOG_CURRENT_EXCEPTION();\r
263                 }\r
264                 \r
265                 send(is_running_ , false);\r
266                 send(target_, core::basic_frame::eof());\r
267 \r
268                 done();\r
269         }\r
270                         \r
271         void push_video(const safe_ptr<AVFrame>& video_frame)\r
272         {                               \r
273                 if(video_frame == eof_video() || video_frame == empty_video())\r
274                 {\r
275                         send(video_, video_frame);\r
276                         return;\r
277                 }\r
278                                 \r
279                 if(video_frame == loop_video())         \r
280                         return; \r
281                 \r
282                 if(display_mode_ == display_mode::invalid)\r
283                 {\r
284                         if(auto_transcode_)\r
285                         {\r
286                                 auto in_mode = get_mode(*video_frame);\r
287                                 display_mode_ = get_display_mode(in_mode, in_fps_, format_desc_.field_mode, format_desc_.fps);\r
288                         \r
289                                 if(display_mode_ == display_mode::simple && in_mode != core::field_mode::progressive && format_desc_.field_mode != core::field_mode::progressive && video_frame->height != static_cast<int>(format_desc_.height))\r
290                                         display_mode_ = display_mode::deinterlace_bob_reinterlace; // The frame will most likely be scaled, we need to deinterlace->reinterlace \r
291                                 \r
292                                 if(display_mode_ == display_mode::deinterlace)\r
293                                         filter_ = filter(L"YADIF=0:-1");\r
294                                 else if(display_mode_ == display_mode::deinterlace_bob || display_mode_ == display_mode::deinterlace_bob_reinterlace)\r
295                                         filter_ = filter(L"YADIF=1:-1");\r
296                         }\r
297                         else\r
298                                 display_mode_ = display_mode::simple;\r
299 \r
300                         if(display_mode_ == display_mode::invalid)\r
301                         {\r
302                                 CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";\r
303                                 display_mode_ = display_mode::simple;\r
304                         }\r
305 \r
306                         // copy <= We need to release frames\r
307                         if(display_mode_ != display_mode::simple && filter_.filter_str().empty())\r
308                                 filter_ = filter(L"copy"); \r
309 \r
310                         CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode_);\r
311                 }\r
312                                                 \r
313                 //if(hints & core::frame_producer::ALPHA_HINT)\r
314                 //      video_frame->format = make_alpha_format(video_frame->format);\r
315                 \r
316                 auto format = video_frame->format;\r
317                 if(video_frame->format == CASPAR_PIX_FMT_LUMA) // CASPAR_PIX_FMT_LUMA is not valid for filter, change it to GRAY8\r
318                         video_frame->format = PIX_FMT_GRAY8;\r
319 \r
320                 filter_.push(video_frame);\r
321 \r
322                 BOOST_FOREACH(auto av_frame, filter_.poll_all())\r
323                 {               \r
324                         av_frame->format = format;                      \r
325                         send(video_, av_frame);\r
326                 }\r
327         }\r
328 \r
329         void push_audio(const safe_ptr<core::audio_buffer>& audio_samples)\r
330         {\r
331                 if(audio_samples == eof_audio() || audio_samples == empty_audio())\r
332                 {\r
333                         send(audio_, audio_samples);\r
334                         return;\r
335                 }\r
336 \r
337                 if(audio_samples == loop_audio())                       \r
338                         return;         \r
339 \r
340                 audio_data_.insert(audio_data_.end(), audio_samples->begin(), audio_samples->end());\r
341                 \r
342                 while(audio_data_.size() >= format_desc_.audio_samples_per_frame)\r
343                 {\r
344                         auto begin = audio_data_.begin(); \r
345                         auto end   = begin + format_desc_.audio_samples_per_frame;\r
346                                         \r
347                         send(audio_, make_safe<core::audio_buffer>(begin, end));\r
348 \r
349                         audio_data_.erase(begin, end);\r
350                 }\r
351         }\r
352                                         \r
353         int64_t calc_nb_frames(int64_t nb_frames) const\r
354         {\r
355                 switch(display_mode_)\r
356                 {\r
357                 case display_mode::interlace:\r
358                 case display_mode::half:\r
359                         return nb_frames/2;\r
360                 case display_mode::duplicate:\r
361                 case display_mode::deinterlace_bob:\r
362                         return nb_frames*2;\r
363                 default:\r
364                         return nb_frames;\r
365                 }\r
366         }\r
367 };\r
368 \r
369 frame_muxer2::frame_muxer2(video_source_t* video_source, \r
370                                                    audio_source_t* audio_source,\r
371                                                    target_t& target,\r
372                                                    double in_fps, \r
373                                                    const safe_ptr<core::frame_factory>& frame_factory)\r
374         : impl_(new implementation(video_source, audio_source, target, in_fps, frame_factory))\r
375 {\r
376 }\r
377 \r
378 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const\r
379 {\r
380         return impl_->calc_nb_frames(nb_frames);\r
381 }\r
382 \r
383 }}