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
135 if(!audio_data_.empty())
\r
137 CASPAR_LOG(info) << L"[frame_muxer] Truncating audio: " << audio_data_.size();
\r
138 audio_data_.clear();
\r
142 if(audio == eof_audio())
\r
145 audio_data_.insert(audio_data_.end(), audio->begin(), audio->end());
\r
148 return receive_audio();
\r
153 win32_exception::install_handler();
\r
157 while(is_running_ && display())
\r
163 CASPAR_LOG_CURRENT_EXCEPTION();
\r
166 send(target_, frame_muxer2::target_element_t(core::basic_frame::eof(), ticket_t()));
\r
173 auto ticket = governor_.acquire();
\r
175 auto video = receive_video();
\r
179 auto audio = receive_audio();
\r
183 video->audio_data() = std::move(*audio);
\r
185 switch(display_mode_.value())
\r
187 case display_mode::simple:
\r
188 case display_mode::deinterlace:
\r
189 case display_mode::deinterlace_bob:
\r
191 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
195 case display_mode::duplicate:
\r
197 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
199 auto video2 = make_safe<core::write_frame>(*video);
\r
200 auto audio2 = receive_audio();
\r
204 video2->audio_data() = std::move(*audio2);
\r
205 send(target_, frame_muxer2::target_element_t(video2, ticket));
\r
208 return audio2 != nullptr;
\r
210 case display_mode::half:
\r
212 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
213 auto video2 = receive_video(); // throw away
\r
215 return video2 != nullptr;
\r
217 case display_mode::deinterlace_bob_reinterlace:
\r
218 case display_mode::interlace:
\r
220 std::shared_ptr<core::basic_frame> video2 = receive_video();
\r
222 video2 = core::basic_frame::empty();
\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
227 return video2 != nullptr;
\r
230 BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));
\r
234 void initialize_display_mode(AVFrame& frame)
\r
236 auto display_mode = display_mode::invalid;
\r
238 if(auto_transcode_)
\r
240 auto mode = get_mode(frame);
\r
241 auto fps = in_fps_;
\r
243 if(is_deinterlacing(filter_str_))
\r
244 mode = core::field_mode::progressive;
\r
246 if(is_double_rate(filter_str_))
\r
249 display_mode = get_display_mode(mode, fps, format_desc_.field_mode, format_desc_.fps);
\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
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
260 display_mode = display_mode::simple;
\r
262 if(display_mode == display_mode::invalid)
\r
264 CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";
\r
265 display_mode = display_mode::simple;
\r
268 send(filter_, make_safe<filter>(filter_str_));
\r
270 CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode);
\r
272 send(display_mode_, display_mode);
\r
275 int64_t calc_nb_frames(int64_t nb_frames) const
\r
277 switch(display_mode_.value()) // Take into account transformation in run.
\r
279 case display_mode::deinterlace_bob_reinterlace:
\r
280 case display_mode::interlace:
\r
281 case display_mode::half:
\r
284 case display_mode::duplicate:
\r
289 if(is_double_rate(widen(filter_.value()->filter_str()))) // Take into account transformations in filter.
\r
296 frame_muxer2::frame_muxer2(video_source_t* video_source,
\r
297 audio_source_t* audio_source,
\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
306 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const
\r
308 return impl_->calc_nb_frames(nb_frames);
\r