2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Robert Nagy, ronag89@gmail.com
22 #include "../StdAfx.h"
24 #include "ffmpeg_producer.h"
26 #include "../ffmpeg_error.h"
27 #include "../ffmpeg.h"
29 #include "muxer/frame_muxer.h"
30 #include "input/input.h"
31 #include "util/util.h"
32 #include "audio/audio_decoder.h"
33 #include "video/video_decoder.h"
35 #include <common/env.h>
36 #include <common/log.h>
37 #include <common/param.h>
38 #include <common/diagnostics/graph.h>
39 #include <common/future.h>
40 #include <common/timer.h>
41 #include <common/assert.h>
43 #include <core/video_format.h>
44 #include <core/producer/frame_producer.h>
45 #include <core/frame/audio_channel_layout.h>
46 #include <core/frame/frame_factory.h>
47 #include <core/frame/draw_frame.h>
48 #include <core/frame/frame_transform.h>
49 #include <core/monitor/monitor.h>
50 #include <core/help/help_repository.h>
51 #include <core/help/help_sink.h>
53 #include <boost/algorithm/string.hpp>
54 #include <boost/filesystem.hpp>
55 #include <boost/property_tree/ptree.hpp>
56 #include <boost/regex.hpp>
57 #include <boost/thread/future.hpp>
59 #include <tbb/parallel_invoke.h>
65 namespace caspar { namespace ffmpeg {
67 struct seek_out_of_range : virtual user_error {};
69 std::wstring get_relative_or_original(
70 const std::wstring& filename,
71 const boost::filesystem::path& relative_to)
73 boost::filesystem::path file(filename);
74 auto result = file.filename().wstring();
76 boost::filesystem::path current_path = file;
80 current_path = current_path.parent_path();
82 if (boost::filesystem::equivalent(current_path, relative_to))
85 if (current_path.empty())
88 result = current_path.filename().wstring() + L"/" + result;
94 struct ffmpeg_producer : public core::frame_producer_base
96 spl::shared_ptr<core::monitor::subject> monitor_subject_;
97 const std::wstring filename_;
98 const std::wstring path_relative_to_media_ = get_relative_or_original(filename_, env::media_folder());
100 const spl::shared_ptr<diagnostics::graph> graph_;
102 const spl::shared_ptr<core::frame_factory> frame_factory_;
103 const core::video_format_desc format_desc_;
107 const double fps_ = read_fps(input_.context(), format_desc_.fps);
108 const uint32_t start_;
110 std::unique_ptr<video_decoder> video_decoder_;
111 std::unique_ptr<audio_decoder> audio_decoder_;
112 std::unique_ptr<frame_muxer> muxer_;
113 core::constraints constraints_;
115 core::draw_frame last_frame_ = core::draw_frame::empty();
117 boost::optional<uint32_t> seek_target_;
120 explicit ffmpeg_producer(
121 const spl::shared_ptr<core::frame_factory>& frame_factory,
122 const core::video_format_desc& format_desc,
123 const std::wstring& channel_layout_spec,
124 const std::wstring& filename,
125 const std::wstring& filter,
129 : filename_(filename)
130 , frame_factory_(frame_factory)
131 , format_desc_(format_desc)
132 , input_(graph_, filename_, loop, start, length)
133 , fps_(read_fps(input_.context(), format_desc_.fps))
136 graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
137 graph_->set_color("underflow", diagnostics::color(0.6f, 0.3f, 0.9f));
138 diagnostics::register_graph(graph_);
143 video_decoder_.reset(new video_decoder(input_));
144 video_decoder_->monitor_output().attach_parent(monitor_subject_);
145 constraints_.width.set(video_decoder_->width());
146 constraints_.height.set(video_decoder_->height());
148 if (is_logging_quiet_for_thread())
149 CASPAR_LOG(debug) << print() << L" " << video_decoder_->print();
151 CASPAR_LOG(info) << print() << L" " << video_decoder_->print();
153 catch(averror_stream_not_found&)
155 CASPAR_LOG(debug) << print() << " No video-stream found. Running without video.";
159 CASPAR_LOG_CURRENT_EXCEPTION();
160 CASPAR_LOG(warning) << print() << "Failed to open video-stream. Running without video.";
163 auto channel_layout = core::audio_channel_layout::invalid();
167 audio_decoder_ .reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
168 audio_decoder_->monitor_output().attach_parent(monitor_subject_);
170 channel_layout = audio_decoder_->channel_layout();
172 CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
174 catch(averror_stream_not_found&)
176 //CASPAR_LOG(warning) << print() << " No audio-stream found. Running without audio.";
180 CASPAR_LOG_CURRENT_EXCEPTION();
181 CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";
184 if (start_ > file_nb_frames())
185 CASPAR_THROW_EXCEPTION(seek_out_of_range() << msg_info("SEEK out of range"));
187 muxer_.reset(new frame_muxer(fps_, frame_factory, format_desc_, channel_layout, filter));
191 if (is_logging_quiet_for_thread())
192 CASPAR_LOG(debug) << print() << L" Initialized";
194 CASPAR_LOG(info) << print() << L" Initialized";
199 core::draw_frame receive_impl() override
201 auto frame = core::draw_frame::late();
203 caspar::timer frame_timer;
211 last_frame_ = frame = std::move(muxer_->front());
214 else if (!input_.eof())
215 graph_->set_tag(diagnostics::tag_severity::WARNING, "underflow");
217 graph_->set_value("frame-time", frame_timer.elapsed()*format_desc_.fps*0.5);
219 << core::monitor::message("/profiler/time") % frame_timer.elapsed() % (1.0/format_desc_.fps);
221 << core::monitor::message("/file/frame") % static_cast<int32_t>(file_frame_number())
222 % static_cast<int32_t>(file_nb_frames())
223 << core::monitor::message("/file/fps") % fps_
224 << core::monitor::message("/file/path") % path_relative_to_media_
225 << core::monitor::message("/loop") % input_.loop();
230 core::draw_frame last_frame() override
233 return core::draw_frame::still(last_frame_);
236 core::constraints& pixel_constraints() override
241 uint32_t nb_frames() const override
244 return std::numeric_limits<uint32_t>::max();
246 uint32_t nb_frames = file_nb_frames();
248 nb_frames = std::min(input_.length(), nb_frames);
249 nb_frames = muxer_->calc_nb_frames(nb_frames);
251 return nb_frames > start_ ? nb_frames - start_ : 0;
254 uint32_t file_nb_frames() const
256 uint32_t file_nb_frames = 0;
257 file_nb_frames = std::max(file_nb_frames, video_decoder_ ? video_decoder_->nb_frames() : 0);
258 file_nb_frames = std::max(file_nb_frames, audio_decoder_ ? audio_decoder_->nb_frames() : 0);
259 return file_nb_frames;
262 uint32_t file_frame_number() const
264 return video_decoder_ ? video_decoder_->file_frame_number() : 0;
267 std::future<std::wstring> call(const std::vector<std::wstring>& params) override
269 static const boost::wregex loop_exp(LR"(LOOP\s*(?<VALUE>\d?)?)", boost::regex::icase);
270 static const boost::wregex seek_exp(LR"(SEEK\s+(?<VALUE>\d+))", boost::regex::icase);
271 static const boost::wregex length_exp(LR"(LENGTH\s+(?<VALUE>\d+)?)", boost::regex::icase);
272 static const boost::wregex start_exp(LR"(START\\s+(?<VALUE>\\d+)?)", boost::regex::icase);
274 auto param = boost::algorithm::join(params, L" ");
279 if(boost::regex_match(param, what, loop_exp))
281 auto value = what["VALUE"].str();
283 input_.loop(boost::lexical_cast<bool>(value));
284 result = boost::lexical_cast<std::wstring>(loop());
286 else if(boost::regex_match(param, what, seek_exp))
288 auto value = what["VALUE"].str();
289 seek(boost::lexical_cast<uint32_t>(value));
291 else if(boost::regex_match(param, what, length_exp))
293 auto value = what["VALUE"].str();
295 length(boost::lexical_cast<uint32_t>(value));
296 result = boost::lexical_cast<std::wstring>(length());
298 else if(boost::regex_match(param, what, start_exp))
300 auto value = what["VALUE"].str();
302 start(boost::lexical_cast<uint32_t>(value));
303 result = boost::lexical_cast<std::wstring>(start());
306 CASPAR_THROW_EXCEPTION(invalid_argument());
308 return make_ready_future(std::move(result));
311 std::wstring print() const override
313 return L"ffmpeg[" + boost::filesystem::path(filename_).filename().wstring() + L"|"
314 + print_mode() + L"|"
315 + boost::lexical_cast<std::wstring>(file_frame_number()) + L"/" + boost::lexical_cast<std::wstring>(file_nb_frames()) + L"]";
318 std::wstring name() const override
323 boost::property_tree::wptree info() const override
325 boost::property_tree::wptree info;
326 info.add(L"type", L"ffmpeg");
327 info.add(L"filename", filename_);
328 info.add(L"width", video_decoder_ ? video_decoder_->width() : 0);
329 info.add(L"height", video_decoder_ ? video_decoder_->height() : 0);
330 info.add(L"progressive", video_decoder_ ? video_decoder_->is_progressive() : 0);
331 info.add(L"fps", fps_);
332 info.add(L"loop", input_.loop());
333 info.add(L"frame-number", frame_number());
334 auto nb_frames2 = nb_frames();
335 info.add(L"nb-frames", nb_frames2 == std::numeric_limits<int64_t>::max() ? -1 : nb_frames2);
336 info.add(L"file-frame-number", file_frame_number());
337 info.add(L"file-nb-frames", file_nb_frames());
341 core::monitor::subject& monitor_output()
343 return *monitor_subject_;
350 for(int n = 0; n < 8 && (last_frame_ == core::draw_frame::empty() || (seek_target_ && file_frame_number() != *seek_target_+2)); ++n)
355 last_frame_ = muxer_->front();
356 seek_target_.reset();
361 void loop(bool value)
368 return input_.loop();
371 void length(uint32_t value)
373 input_.length(value);
378 return input_.length();
381 void start(uint32_t value)
388 return input_.start();
391 void seek(uint32_t target)
393 if (target > file_nb_frames())
394 CASPAR_THROW_EXCEPTION(seek_out_of_range() << msg_info("SEEK out of range"));
396 seek_target_ = target;
398 input_.seek(*seek_target_);
402 std::wstring print_mode() const
404 return ffmpeg::print_mode(video_decoder_ ? video_decoder_->width() : 0,
405 video_decoder_ ? video_decoder_->height() : 0,
407 video_decoder_ ? !video_decoder_->is_progressive() : false);
410 void decode_next_frame()
412 for(int n = 0; n < 32 && muxer_->empty(); ++n)
414 if(!muxer_->video_ready())
415 muxer_->push_video(video_decoder_ ? (*video_decoder_)() : create_frame());
416 if(!muxer_->audio_ready())
417 muxer_->push_audio(audio_decoder_ ? (*audio_decoder_)() : create_frame());
420 graph_->set_text(print());
424 void describe_producer(core::help_sink& sink, const core::help_repository& repo)
426 sink.short_description(L"A producer for playing media files supported by FFmpeg.");
427 sink.syntax(L"[clip:string] {[loop:LOOP]} {START,SEEK [start:int]} {LENGTH [start:int]} {FILTER [filter:string]} {CHANNEL_LAYOUT [channel_layout:string]}");
429 ->text(L"The FFmpeg Producer can play all media that FFmpeg can play, which includes many ")
430 ->text(L"QuickTime video codec such as Animation, PNG, PhotoJPEG, MotionJPEG, as well as ")
431 ->text(L"H.264, FLV, WMV and several audio codecs as well as uncompressed audio.");
433 ->item(L"clip", L"The file without the file extension to play. It should reside under the media folder.")
434 ->item(L"loop", L"Will cause the media file to loop between start and start + length")
435 ->item(L"start", L"Optionally sets the start frame. 0 by default. If loop is specified this will be the frame where it starts over again.")
436 ->item(L"length", L"Optionally sets the length of the clip. If not specified the clip will be played to the end. If loop is specified the file will jump to start position once this number of frames has been played.")
437 ->item(L"filter", L"If specified, will be used as an FFmpeg video filter.")
438 ->item(L"channel_layout",
439 L"Optionally override the automatically deduced audio channel layout. "
440 L"Either a named layout as specified in casparcg.config or in the format [type:string]:[channel_order:string] for a custom layout.");
441 sink.para()->text(L"Examples:");
442 sink.example(L">> PLAY 1-10 folder/clip", L"to play all frames in a clip and stop at the last frame.");
443 sink.example(L">> PLAY 1-10 folder/clip LOOP", L"to loop a clip between the first frame and the last frame.");
444 sink.example(L">> PLAY 1-10 folder/clip LOOP START 10", L"to loop a clip between frame 10 and the last frame.");
445 sink.example(L">> PLAY 1-10 folder/clip LOOP START 10 LENGTH 50", L"to loop a clip between frame 10 and frame 60.");
446 sink.example(L">> PLAY 1-10 folder/clip START 10 LENGTH 50", L"to play frames 10-60 in a clip and stop.");
447 sink.example(L">> PLAY 1-10 folder/clip FILTER yadif=1,-1", L"to deinterlace the video.");
448 sink.example(L">> PLAY 1-10 folder/clip CHANNEL_LAYOUT film", L"given the defaults in casparcg.config this will specifies that the clip has 6 audio channels of the type 5.1 and that they are in the order FL FC FR BL BR LFE regardless of what ffmpeg says.");
449 sink.example(L">> PLAY 1-10 folder/clip CHANNEL_LAYOUT \"5.1:LFE FL FC FR BL BR\"", L"specifies that the clip has 6 audio channels of the type 5.1 and that they are in the specified order regardless of what ffmpeg says.");
450 sink.para()->text(L"The FFmpeg producer also supports changing some of the settings via CALL:");
451 sink.example(L">> CALL 1-10 LOOP 1");
452 sink.example(L">> CALL 1-10 START 10");
453 sink.example(L">> CALL 1-10 LENGTH 50");
456 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
458 auto filename = probe_stem(env::media_folder() + L"/" + params.at(0));
461 return core::frame_producer::empty();
463 bool loop = contains_param(L"LOOP", params);
464 auto start = get_param(L"START", params, get_param(L"SEEK", params, static_cast<uint32_t>(0)));
465 auto length = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());
466 auto filter_str = get_param(L"FILTER", params, L"");
467 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params, L"");
469 return create_destroy_proxy(spl::make_shared_ptr(std::make_shared<ffmpeg_producer>(
470 dependencies.frame_factory,
471 dependencies.format_desc,