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 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
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
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
87 std::shared_ptr<core::write_frame> receive_video()
\r
90 return make_safe<core::write_frame>(this);
\r
92 auto video = filter_.has_value() ? filter_.value()->poll() : nullptr;
\r
94 return make_write_frame(this, make_safe_ptr(video), frame_factory_, 0);
\r
96 video = receive(video_source_);
\r
98 if(video == flush_video())
\r
100 if(filter_.has_value())
\r
101 filter_.value()->push(nullptr);
\r
102 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(size_t n_samples)
\r
120 return make_safe<core::audio_buffer>(n_samples, 0);
\r
122 if(audio_data_.size() >= n_samples)
\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
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(n_samples);
\r
162 CASPAR_LOG_CURRENT_EXCEPTION();
\r
165 send(target_, frame_muxer2::target_element_t(core::basic_frame::eof(), ticket_t()));
\r
172 auto ticket = governor_.acquire();
\r
174 auto video = receive_video();
\r
178 auto audio = receive_audio(format_desc_.audio_samples_per_frame);
\r
182 video->audio_data() = std::move(*audio);
\r
184 switch(display_mode_.value())
\r
186 case display_mode::simple:
\r
187 case display_mode::deinterlace:
\r
188 case display_mode::deinterlace_bob:
\r
190 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
194 case display_mode::duplicate:
\r
196 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
198 auto video2 = make_safe<core::write_frame>(*video);
\r
199 auto audio2 = receive_audio(format_desc_.audio_samples_per_frame);
\r
203 video2->audio_data() = std::move(*audio2);
\r
204 send(target_, frame_muxer2::target_element_t(video2, ticket));
\r
207 return audio2 != nullptr;
\r
209 case display_mode::half:
\r
211 send(target_, frame_muxer2::target_element_t(video, ticket));
\r
212 auto video2 = receive_video();
\r
214 return video2 != nullptr;
\r
216 case display_mode::deinterlace_bob_reinterlace:
\r
217 case display_mode::interlace:
\r
219 std::shared_ptr<core::basic_frame> video2 = receive_video();
\r
221 video2 = core::basic_frame::empty();
\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
226 return video2 != nullptr;
\r
229 BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("invalid display-mode"));
\r
233 void initialize_display_mode(AVFrame& frame)
\r
235 auto display_mode = display_mode::invalid;
\r
237 if(auto_transcode_)
\r
239 auto mode = get_mode(frame);
\r
240 auto fps = in_fps_;
\r
242 if(is_deinterlacing(filter_str_))
\r
243 mode = core::field_mode::progressive;
\r
245 if(is_double_rate(filter_str_))
\r
248 display_mode = get_display_mode(mode, fps, format_desc_.field_mode, format_desc_.fps);
\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
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
259 display_mode = display_mode::simple;
\r
261 if(display_mode == display_mode::invalid)
\r
263 CASPAR_LOG(warning) << L"[frame_muxer] Failed to detect display-mode.";
\r
264 display_mode = display_mode::simple;
\r
267 send(filter_, make_safe<filter>(filter_str_));
\r
269 CASPAR_LOG(info) << "[frame_muxer] " << display_mode::print(display_mode);
\r
271 send(display_mode_, display_mode);
\r
274 int64_t calc_nb_frames(int64_t nb_frames) const
\r
276 switch(display_mode_.value()) // Take into account transformation in run.
\r
278 case display_mode::deinterlace_bob_reinterlace:
\r
279 case display_mode::interlace:
\r
280 case display_mode::half:
\r
283 case display_mode::duplicate:
\r
288 if(is_double_rate(widen(filter_.value()->filter_str()))) // Take into account transformations in filter.
\r
295 frame_muxer2::frame_muxer2(video_source_t* video_source,
\r
296 audio_source_t* audio_source,
\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
305 int64_t frame_muxer2::calc_nb_frames(int64_t nb_frames) const
\r
307 return impl_->calc_nb_frames(nb_frames);
\r