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_disabled_for_thread())
149 CASPAR_LOG(info) << print() << L" " << video_decoder_->print();
151 catch(averror_stream_not_found&)
153 //CASPAR_LOG(warning) << print() << " No video-stream found. Running without video.";
157 CASPAR_LOG_CURRENT_EXCEPTION();
158 CASPAR_LOG(warning) << print() << "Failed to open video-stream. Running without video.";
161 auto channel_layout = core::audio_channel_layout::invalid();
165 audio_decoder_ .reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
166 audio_decoder_->monitor_output().attach_parent(monitor_subject_);
168 channel_layout = audio_decoder_->channel_layout();
170 CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
172 catch(averror_stream_not_found&)
174 //CASPAR_LOG(warning) << print() << " No audio-stream found. Running without audio.";
178 CASPAR_LOG_CURRENT_EXCEPTION();
179 CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";
182 if (start_ > file_nb_frames())
183 CASPAR_THROW_EXCEPTION(seek_out_of_range() << msg_info("SEEK out of range"));
185 muxer_.reset(new frame_muxer(fps_, frame_factory, format_desc_, channel_layout, filter));
189 if (!is_logging_disabled_for_thread())
190 CASPAR_LOG(info) << print() << L" Initialized";
195 core::draw_frame receive_impl() override
197 auto frame = core::draw_frame::late();
199 caspar::timer frame_timer;
207 last_frame_ = frame = std::move(muxer_->front());
210 else if (!input_.eof())
211 graph_->set_tag(diagnostics::tag_severity::WARNING, "underflow");
213 graph_->set_value("frame-time", frame_timer.elapsed()*format_desc_.fps*0.5);
215 << core::monitor::message("/profiler/time") % frame_timer.elapsed() % (1.0/format_desc_.fps);
217 << core::monitor::message("/file/frame") % static_cast<int32_t>(file_frame_number())
218 % static_cast<int32_t>(file_nb_frames())
219 << core::monitor::message("/file/fps") % fps_
220 << core::monitor::message("/file/path") % path_relative_to_media_
221 << core::monitor::message("/loop") % input_.loop();
226 core::draw_frame last_frame() override
229 return core::draw_frame::still(last_frame_);
232 core::constraints& pixel_constraints() override
237 uint32_t nb_frames() const override
240 return std::numeric_limits<uint32_t>::max();
242 uint32_t nb_frames = file_nb_frames();
244 nb_frames = std::min(input_.length(), nb_frames);
245 nb_frames = muxer_->calc_nb_frames(nb_frames);
247 return nb_frames > start_ ? nb_frames - start_ : 0;
250 uint32_t file_nb_frames() const
252 uint32_t file_nb_frames = 0;
253 file_nb_frames = std::max(file_nb_frames, video_decoder_ ? video_decoder_->nb_frames() : 0);
254 file_nb_frames = std::max(file_nb_frames, audio_decoder_ ? audio_decoder_->nb_frames() : 0);
255 return file_nb_frames;
258 uint32_t file_frame_number() const
260 return video_decoder_ ? video_decoder_->file_frame_number() : 0;
263 std::future<std::wstring> call(const std::vector<std::wstring>& params) override
265 static const boost::wregex loop_exp(LR"(LOOP\s*(?<VALUE>\d?)?)", boost::regex::icase);
266 static const boost::wregex seek_exp(LR"(SEEK\s+(?<VALUE>\d+))", boost::regex::icase);
267 static const boost::wregex length_exp(LR"(LENGTH\s+(?<VALUE>\d+)?)", boost::regex::icase);
268 static const boost::wregex start_exp(LR"(START\\s+(?<VALUE>\\d+)?)", boost::regex::icase);
270 auto param = boost::algorithm::join(params, L" ");
275 if(boost::regex_match(param, what, loop_exp))
277 auto value = what["VALUE"].str();
279 input_.loop(boost::lexical_cast<bool>(value));
280 result = boost::lexical_cast<std::wstring>(loop());
282 else if(boost::regex_match(param, what, seek_exp))
284 auto value = what["VALUE"].str();
285 seek(boost::lexical_cast<uint32_t>(value));
287 else if(boost::regex_match(param, what, length_exp))
289 auto value = what["VALUE"].str();
291 length(boost::lexical_cast<uint32_t>(value));
292 result = boost::lexical_cast<std::wstring>(length());
294 else if(boost::regex_match(param, what, start_exp))
296 auto value = what["VALUE"].str();
298 start(boost::lexical_cast<uint32_t>(value));
299 result = boost::lexical_cast<std::wstring>(start());
302 CASPAR_THROW_EXCEPTION(invalid_argument());
304 return make_ready_future(std::move(result));
307 std::wstring print() const override
309 return L"ffmpeg[" + boost::filesystem::path(filename_).filename().wstring() + L"|"
310 + print_mode() + L"|"
311 + boost::lexical_cast<std::wstring>(file_frame_number()) + L"/" + boost::lexical_cast<std::wstring>(file_nb_frames()) + L"]";
314 std::wstring name() const override
319 boost::property_tree::wptree info() const override
321 boost::property_tree::wptree info;
322 info.add(L"type", L"ffmpeg");
323 info.add(L"filename", filename_);
324 info.add(L"width", video_decoder_ ? video_decoder_->width() : 0);
325 info.add(L"height", video_decoder_ ? video_decoder_->height() : 0);
326 info.add(L"progressive", video_decoder_ ? video_decoder_->is_progressive() : 0);
327 info.add(L"fps", fps_);
328 info.add(L"loop", input_.loop());
329 info.add(L"frame-number", frame_number());
330 auto nb_frames2 = nb_frames();
331 info.add(L"nb-frames", nb_frames2 == std::numeric_limits<int64_t>::max() ? -1 : nb_frames2);
332 info.add(L"file-frame-number", file_frame_number());
333 info.add(L"file-nb-frames", file_nb_frames());
337 core::monitor::subject& monitor_output()
339 return *monitor_subject_;
346 for(int n = 0; n < 8 && (last_frame_ == core::draw_frame::empty() || (seek_target_ && file_frame_number() != *seek_target_+2)); ++n)
351 last_frame_ = muxer_->front();
352 seek_target_.reset();
357 void loop(bool value)
364 return input_.loop();
367 void length(uint32_t value)
369 input_.length(value);
374 return input_.length();
377 void start(uint32_t value)
384 return input_.start();
387 void seek(uint32_t target)
389 if (target > file_nb_frames())
390 CASPAR_THROW_EXCEPTION(seek_out_of_range() << msg_info("SEEK out of range"));
392 seek_target_ = target;
394 input_.seek(*seek_target_);
398 std::wstring print_mode() const
400 return ffmpeg::print_mode(video_decoder_ ? video_decoder_->width() : 0,
401 video_decoder_ ? video_decoder_->height() : 0,
403 video_decoder_ ? !video_decoder_->is_progressive() : false);
406 void decode_next_frame()
408 for(int n = 0; n < 32 && muxer_->empty(); ++n)
410 if(!muxer_->video_ready())
411 muxer_->push_video(video_decoder_ ? (*video_decoder_)() : create_frame());
412 if(!muxer_->audio_ready())
413 muxer_->push_audio(audio_decoder_ ? (*audio_decoder_)() : create_frame());
416 graph_->set_text(print());
420 void describe_producer(core::help_sink& sink, const core::help_repository& repo)
422 sink.short_description(L"A producer for playing media files supported by FFmpeg.");
423 sink.syntax(L"[clip:string] {[loop:LOOP]} {START,SEEK [start:int]} {LENGTH [start:int]} {FILTER [filter:string]} {CHANNEL_LAYOUT [channel_layout:string]}");
425 ->text(L"The FFmpeg Producer can play all media that FFmpeg can play, which includes many ")
426 ->text(L"QuickTime video codec such as Animation, PNG, PhotoJPEG, MotionJPEG, as well as ")
427 ->text(L"H.264, FLV, WMV and several audio codecs as well as uncompressed audio.");
429 ->item(L"clip", L"The file without the file extension to play. It should reside under the media folder.")
430 ->item(L"loop", L"Will cause the media file to loop between start and start + length")
431 ->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.")
432 ->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.")
433 ->item(L"filter", L"If specified, will be used as an FFmpeg video filter.")
434 ->item(L"channel_layout",
435 L"Optionally override the automatically deduced audio channel layout. "
436 L"Either a named layout as specified in casparcg.config or in the format [type:string]:[channel_order:string] for a custom layout.");
437 sink.para()->text(L"Examples:");
438 sink.example(L">> PLAY 1-10 folder/clip", L"to play all frames in a clip and stop at the last frame.");
439 sink.example(L">> PLAY 1-10 folder/clip LOOP", L"to loop a clip between the first frame and the last frame.");
440 sink.example(L">> PLAY 1-10 folder/clip LOOP START 10", L"to loop a clip between frame 10 and the last frame.");
441 sink.example(L">> PLAY 1-10 folder/clip LOOP START 10 LENGTH 50", L"to loop a clip between frame 10 and frame 60.");
442 sink.example(L">> PLAY 1-10 folder/clip START 10 LENGTH 50", L"to play frames 10-60 in a clip and stop.");
443 sink.example(L">> PLAY 1-10 folder/clip FILTER yadif=1,-1", L"to deinterlace the video.");
444 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.");
445 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.");
446 sink.para()->text(L"The FFmpeg producer also supports changing some of the settings via CALL:");
447 sink.example(L">> CALL 1-10 LOOP 1");
448 sink.example(L">> CALL 1-10 START 10");
449 sink.example(L">> CALL 1-10 LENGTH 50");
452 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
454 auto filename = probe_stem(env::media_folder() + L"/" + params.at(0));
457 return core::frame_producer::empty();
459 bool loop = contains_param(L"LOOP", params);
460 auto start = get_param(L"START", params, get_param(L"SEEK", params, static_cast<uint32_t>(0)));
461 auto length = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());
462 auto filter_str = get_param(L"FILTER", params, L"");
463 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params, L"");
465 return create_destroy_proxy(spl::make_shared_ptr(std::make_shared<ffmpeg_producer>(
466 dependencies.frame_factory,
467 dependencies.format_desc,