1 #include "../../StdAfx.h"
\r
3 #include "frame_muxer.h"
\r
5 #include "display_mode.h"
\r
7 #include "../filter/filter.h"
\r
8 #include "../util/util.h"
\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
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
20 #include <boost/range/algorithm_ext/push_back.hpp>
\r
22 #if defined(_MSC_VER)
\r
23 #pragma warning (push)
\r
24 #pragma warning (disable : 4244)
\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
33 #if defined(_MSC_VER)
\r
34 #pragma warning (pop)
\r
39 using namespace Concurrency;
\r
41 namespace caspar { namespace ffmpeg {
\r
43 struct frame_muxer2::implementation : public Concurrency::agent, boost::noncopyable
\r
45 frame_muxer2::video_source_t* video_source_;
\r
46 frame_muxer2::audio_source_t* audio_source_;
\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
54 mutable single_assignment<safe_ptr<filter>> filter_;
\r
55 const safe_ptr<core::frame_factory> frame_factory_;
\r
57 core::audio_buffer audio_data_;
\r
59 std::wstring filter_str_;
\r
63 tbb::atomic<bool> is_running_;
\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
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
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
86 is_running_ = false;
\r
91 std::shared_ptr<core::write_frame> receive_video()
\r
94 return make_safe<core::write_frame>(this);
\r
96 auto video = filter_.has_value() ? filter_.value()->poll() : nullptr;
\r
98 return make_write_frame(this, make_safe_ptr(video), frame_factory_, 0);
\r
100 video = receive(video_source_);
\r
102 if(video == flush_video())
\r
103 return receive_video();
\r
105 if(video == eof_video())
\r
108 if(!display_mode_.has_value())
\r
109 initialize_display_mode(*video);
\r
111 filter_.value()->push(video);
\r
114 return receive_video();
\r
117 std::shared_ptr<core::audio_buffer> receive_audio()
\r
120 return make_safe<core::audio_buffer>(format_desc_.audio_samples_per_frame, 0);
\r
122 if(audio_data_.size() >= format_desc_.audio_samples_per_frame)
\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
131 std::shared_ptr<core::audio_buffer> audio = receive(audio_source_);
\r
133 if(audio == flush_audio())
\r
134 return receive_audio();
\r
136 if(audio == eof_audio())
\r
139 audio_data_.insert(audio_data_.end(), audio->begin(), audio->end());
\r
142 return receive_audio();
\r
147 win32_exception::install_handler();
\r
151 while(is_running_ && display())
\r
157 CASPAR_LOG_CURRENT_EXCEPTION();
\r
160 send(target_, frame_muxer2::target_element_t(core::basic_frame::eof(), ticket_t()));
\r
167 auto ticket = governor_.acquire();
\r
169 auto video = receive_video();
\r
173 auto audio = receive_audio();
\r
177 video->audio_data() = std::move(*audio);
\r
179 switch(display_mode_.value())
\r
181 case display_mode::simple:
\r
182 case display_mode::deinterlace:
\r
183 case display_mode::deinterlace_bob:
\r
185 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
189 case display_mode::duplicate:
\r
191 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
193 auto video2 = make_safe<core::write_frame>(*video);
\r
194 auto audio2 = receive_audio();
\r
198 video2->audio_data() = std::move(*audio2);
\r
199 send(target_, frame_muxer2::target_element_t(video2, ticket));
\r
202 return audio2 != nullptr;
\r
204 case display_mode::half:
\r
206 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
207 auto video2 = receive_video(); // throw away
\r
209 return video2 != nullptr;
\r
211 case display_mode::deinterlace_bob_reinterlace:
\r
212 case display_mode::interlace:
\r
214 std::shared_ptr<core::basic_frame> video2 = receive_video();
\r
216 video2 = core::basic_frame::empty();
\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
221 return video2 != nullptr;
\r
224 BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));
\r
228 void initialize_display_mode(AVFrame& frame)
\r
230 auto display_mode = display_mode::invalid;
\r
232 if(auto_transcode_)
\r
234 auto mode = get_mode(frame);
\r
235 auto fps = in_fps_;
\r
237 if(is_deinterlacing(filter_str_))
\r
238 mode = core::field_mode::progressive;
\r
240 if(is_double_rate(filter_str_))
\r
243 display_mode = get_display_mode(mode, fps, format_desc_.field_mode, format_desc_.fps);
\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
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
254 display_mode = display_mode::simple;
\r
256 if(display_mode == display_mode::invalid)
\r
258 CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";
\r
259 display_mode = display_mode::simple;
\r
262 send(filter_, make_safe<filter>(filter_str_));
\r
264 CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode);
\r
266 send(display_mode_, display_mode);
\r
269 int64_t calc_nb_frames(int64_t nb_frames) const
\r
271 switch(display_mode_.value()) // Take into account transformation in run.
\r
273 case display_mode::deinterlace_bob_reinterlace:
\r
274 case display_mode::interlace:
\r
275 case display_mode::half:
\r
278 case display_mode::duplicate:
\r
283 if(is_double_rate(widen(filter_.value()->filter_str()))) // Take into account transformations in filter.
\r
290 frame_muxer2::frame_muxer2(video_source_t* video_source,
\r
291 audio_source_t* audio_source,
\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
300 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const
\r
302 return impl_->calc_nb_frames(nb_frames);
\r