]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/ffmpeg_producer.cpp
* Added logging of severe diagnostics events to log at warning level. graph::set_tag...
[casparcg] / modules / ffmpeg / producer / ffmpeg_producer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "../StdAfx.h"
23
24 #include "ffmpeg_producer.h"
25
26 #include "../ffmpeg_error.h"
27
28 #include "muxer/frame_muxer.h"
29 #include "input/input.h"
30 #include "util/util.h"
31 #include "audio/audio_decoder.h"
32 #include "video/video_decoder.h"
33
34 #include <common/env.h>
35 #include <common/log.h>
36 #include <common/param.h>
37 #include <common/diagnostics/graph.h>
38 #include <common/future.h>
39 #include <common/timer.h>
40 #include <common/assert.h>
41
42 #include <core/video_format.h>
43 #include <core/producer/frame_producer.h>
44 #include <core/frame/audio_channel_layout.h>
45 #include <core/frame/frame_factory.h>
46 #include <core/frame/draw_frame.h>
47 #include <core/frame/frame_transform.h>
48 #include <core/monitor/monitor.h>
49 #include <core/help/help_repository.h>
50 #include <core/help/help_sink.h>
51
52 #include <boost/algorithm/string.hpp>
53 #include <boost/filesystem.hpp>
54 #include <boost/property_tree/ptree.hpp>
55 #include <boost/regex.hpp>
56 #include <boost/thread/future.hpp>
57
58 #include <tbb/parallel_invoke.h>
59
60 #include <limits>
61 #include <memory>
62 #include <queue>
63
64 namespace caspar { namespace ffmpeg {
65
66 struct seek_out_of_range : virtual user_error {};
67
68 std::wstring get_relative_or_original(
69                 const std::wstring& filename,
70                 const boost::filesystem::path& relative_to)
71 {
72         boost::filesystem::path file(filename);
73         auto result = file.filename().wstring();
74
75         boost::filesystem::path current_path = file;
76
77         while (true)
78         {
79                 current_path = current_path.parent_path();
80
81                 if (boost::filesystem::equivalent(current_path, relative_to))
82                         break;
83
84                 if (current_path.empty())
85                         return filename;
86
87                 result = current_path.filename().wstring() + L"/" + result;
88         }
89
90         return result;
91 }
92
93 struct ffmpeg_producer : public core::frame_producer_base
94 {
95         spl::shared_ptr<core::monitor::subject>                 monitor_subject_;
96         const std::wstring                                                              filename_;
97         const std::wstring                                                              path_relative_to_media_ = get_relative_or_original(filename_, env::media_folder());
98         
99         const spl::shared_ptr<diagnostics::graph>               graph_;
100                                         
101         const spl::shared_ptr<core::frame_factory>              frame_factory_;
102         const core::video_format_desc                                   format_desc_;
103
104         input                                                                                   input_; 
105
106         const double                                                                    fps_                                    = read_fps(input_.context(), format_desc_.fps);
107         const uint32_t                                                                  start_;
108                 
109         std::unique_ptr<video_decoder>                                  video_decoder_;
110         std::unique_ptr<audio_decoder>                                  audio_decoder_; 
111         std::unique_ptr<frame_muxer>                                    muxer_;
112         core::constraints                                                               constraints_;
113         
114         core::draw_frame                                                                last_frame_                             = core::draw_frame::empty();
115
116         boost::optional<uint32_t>                                               seek_target_;
117         
118 public:
119         explicit ffmpeg_producer(
120                         const spl::shared_ptr<core::frame_factory>& frame_factory, 
121                         const core::video_format_desc& format_desc,
122                         const std::wstring& channel_layout_spec,
123                         const std::wstring& filename,
124                         const std::wstring& filter,
125                         bool loop,
126                         uint32_t start,
127                         uint32_t length)
128                 : filename_(filename)
129                 , frame_factory_(frame_factory)         
130                 , format_desc_(format_desc)
131                 , input_(graph_, filename_, loop, start, length)
132                 , fps_(read_fps(input_.context(), format_desc_.fps))
133                 , start_(start)
134         {
135                 graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
136                 graph_->set_color("underflow", diagnostics::color(0.6f, 0.3f, 0.9f));   
137                 diagnostics::register_graph(graph_);
138                 
139
140                 try
141                 {
142                         video_decoder_.reset(new video_decoder(input_));
143                         video_decoder_->monitor_output().attach_parent(monitor_subject_);
144                         constraints_.width.set(video_decoder_->width());
145                         constraints_.height.set(video_decoder_->height());
146                         
147                         CASPAR_LOG(info) << print() << L" " << video_decoder_->print();
148                 }
149                 catch(averror_stream_not_found&)
150                 {
151                         //CASPAR_LOG(warning) << print() << " No video-stream found. Running without video.";   
152                 }
153                 catch(...)
154                 {
155                         CASPAR_LOG_CURRENT_EXCEPTION();
156                         CASPAR_LOG(warning) << print() << "Failed to open video-stream. Running without video.";        
157                 }
158
159                 auto channel_layout = core::audio_channel_layout::invalid();
160
161                 try
162                 {
163                         audio_decoder_ .reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
164                         audio_decoder_->monitor_output().attach_parent(monitor_subject_);
165
166                         channel_layout = audio_decoder_->channel_layout();
167                         
168                         CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
169                 }
170                 catch(averror_stream_not_found&)
171                 {
172                         //CASPAR_LOG(warning) << print() << " No audio-stream found. Running without audio.";   
173                 }
174                 catch(...)
175                 {
176                         CASPAR_LOG_CURRENT_EXCEPTION();
177                         CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";               
178                 }
179
180                 if (start_ > file_nb_frames())
181                         CASPAR_THROW_EXCEPTION(seek_out_of_range() << msg_info("SEEK out of range"));
182
183                 muxer_.reset(new frame_muxer(fps_, frame_factory, format_desc_, channel_layout, filter));
184                 
185                 decode_next_frame();
186
187                 CASPAR_LOG(info) << print() << L" Initialized";
188         }
189
190         // frame_producer
191         
192         core::draw_frame receive_impl() override
193         {                               
194                 auto frame = core::draw_frame::late();          
195                 
196                 caspar::timer frame_timer;
197                 
198                 end_seek();
199                                 
200                 decode_next_frame();
201                 
202                 if(!muxer_->empty())
203                 {
204                         last_frame_ = frame = std::move(muxer_->front());
205                         muxer_->pop();
206                 }
207                 else
208                         graph_->set_tag(diagnostics::tag_severity::WARNING, "underflow");
209
210                 graph_->set_value("frame-time", frame_timer.elapsed()*format_desc_.fps*0.5);
211                 *monitor_subject_
212                                 << core::monitor::message("/profiler/time")     % frame_timer.elapsed() % (1.0/format_desc_.fps);                       
213                 *monitor_subject_
214                                 << core::monitor::message("/file/frame")        % static_cast<int32_t>(file_frame_number())
215                                                                                                                         % static_cast<int32_t>(file_nb_frames())
216                                 << core::monitor::message("/file/fps")          % fps_
217                                 << core::monitor::message("/file/path")         % path_relative_to_media_
218                                 << core::monitor::message("/loop")                      % input_.loop();
219                                                 
220                 return frame;
221         }
222
223         core::draw_frame last_frame() override
224         {
225                 end_seek();
226                 return core::draw_frame::still(last_frame_);
227         }
228
229         core::constraints& pixel_constraints() override
230         {
231                 return constraints_;
232         }
233
234         uint32_t nb_frames() const override
235         {
236                 if(input_.loop())
237                         return std::numeric_limits<uint32_t>::max();
238
239                 uint32_t nb_frames = file_nb_frames();
240
241                 nb_frames = std::min(input_.length(), nb_frames);
242                 nb_frames = muxer_->calc_nb_frames(nb_frames);
243                 
244                 return nb_frames > start_ ? nb_frames - start_ : 0;
245         }
246
247         uint32_t file_nb_frames() const
248         {
249                 uint32_t file_nb_frames = 0;
250                 file_nb_frames = std::max(file_nb_frames, video_decoder_ ? video_decoder_->nb_frames() : 0);
251                 file_nb_frames = std::max(file_nb_frames, audio_decoder_ ? audio_decoder_->nb_frames() : 0);
252                 return file_nb_frames;
253         }
254
255         uint32_t file_frame_number() const
256         {
257                 return video_decoder_ ? video_decoder_->file_frame_number() : 0;
258         }
259                 
260         std::future<std::wstring> call(const std::vector<std::wstring>& params) override
261         {
262                 static const boost::wregex loop_exp(LR"(LOOP\s*(?<VALUE>\d?)?)", boost::regex::icase);
263                 static const boost::wregex seek_exp(LR"(SEEK\s+(?<VALUE>\d+))", boost::regex::icase);
264                 static const boost::wregex length_exp(LR"(LENGTH\s+(?<VALUE>\d+)?)", boost::regex::icase);
265                 static const boost::wregex start_exp(LR"(START\\s+(?<VALUE>\\d+)?)", boost::regex::icase);
266
267                 auto param = boost::algorithm::join(params, L" ");
268                 
269                 std::wstring result;
270                         
271                 boost::wsmatch what;
272                 if(boost::regex_match(param, what, loop_exp))
273                 {
274                         auto value = what["VALUE"].str();
275                         if(!value.empty())
276                                 input_.loop(boost::lexical_cast<bool>(value));
277                         result = boost::lexical_cast<std::wstring>(loop());
278                 }
279                 else if(boost::regex_match(param, what, seek_exp))
280                 {
281                         auto value = what["VALUE"].str();
282                         seek(boost::lexical_cast<uint32_t>(value));
283                 }
284                 else if(boost::regex_match(param, what, length_exp))
285                 {
286                         auto value = what["VALUE"].str();
287                         if(!value.empty())
288                                 length(boost::lexical_cast<uint32_t>(value));                   
289                         result = boost::lexical_cast<std::wstring>(length());
290                 }
291                 else if(boost::regex_match(param, what, start_exp))
292                 {
293                         auto value = what["VALUE"].str();
294                         if(!value.empty())
295                                 start(boost::lexical_cast<uint32_t>(value));
296                         result = boost::lexical_cast<std::wstring>(start());
297                 }
298                 else
299                         CASPAR_THROW_EXCEPTION(invalid_argument());
300
301                 return make_ready_future(std::move(result));
302         }
303                                 
304         std::wstring print() const override
305         {
306                 return L"ffmpeg[" + boost::filesystem::path(filename_).filename().wstring() + L"|" 
307                                                   + print_mode() + L"|" 
308                                                   + boost::lexical_cast<std::wstring>(file_frame_number()) + L"/" + boost::lexical_cast<std::wstring>(file_nb_frames()) + L"]";
309         }
310
311         std::wstring name() const override
312         {
313                 return L"ffmpeg";
314         }
315
316         boost::property_tree::wptree info() const override
317         {
318                 boost::property_tree::wptree info;
319                 info.add(L"type",                               L"ffmpeg");
320                 info.add(L"filename",                   filename_);
321                 info.add(L"width",                              video_decoder_ ? video_decoder_->width() : 0);
322                 info.add(L"height",                             video_decoder_ ? video_decoder_->height() : 0);
323                 info.add(L"progressive",                video_decoder_ ? video_decoder_->is_progressive() : 0);
324                 info.add(L"fps",                                fps_);
325                 info.add(L"loop",                               input_.loop());
326                 info.add(L"frame-number",               frame_number());
327                 auto nb_frames2 = nb_frames();
328                 info.add(L"nb-frames",                  nb_frames2 == std::numeric_limits<int64_t>::max() ? -1 : nb_frames2);
329                 info.add(L"file-frame-number",  file_frame_number());
330                 info.add(L"file-nb-frames",             file_nb_frames());
331                 return info;
332         }
333         
334         core::monitor::subject& monitor_output()
335         {
336                 return *monitor_subject_;
337         }
338
339         // ffmpeg_producer
340         
341         void end_seek()
342         {
343                 for(int n = 0; n < 8 && (last_frame_ == core::draw_frame::empty() || (seek_target_ && file_frame_number() != *seek_target_+2)); ++n)
344                 {
345                         decode_next_frame();
346                         if(!muxer_->empty())
347                         {
348                                 last_frame_ = muxer_->front();
349                                 seek_target_.reset();
350                         }
351                 }
352         }
353
354         void loop(bool value)
355         {
356                 input_.loop(value);
357         }
358
359         bool loop() const
360         {
361                 return input_.loop();
362         }
363
364         void length(uint32_t value)
365         {
366                 input_.length(value);
367         }
368
369         uint32_t length()
370         {
371                 return input_.length();
372         }
373         
374         void start(uint32_t value)
375         {
376                 input_.start(value);
377         }
378
379         uint32_t start()
380         {
381                 return input_.start();
382         }
383
384         void seek(uint32_t target)
385         {               
386                 if (target > file_nb_frames())
387                         CASPAR_THROW_EXCEPTION(seek_out_of_range() << msg_info("SEEK out of range"));
388
389                 seek_target_ = target;
390
391                 input_.seek(*seek_target_);
392                 muxer_->clear();
393         }
394
395         std::wstring print_mode() const
396         {
397                 return ffmpeg::print_mode(video_decoder_ ? video_decoder_->width() : 0, 
398                         video_decoder_ ? video_decoder_->height() : 0, 
399                         fps_, 
400                         video_decoder_ ? !video_decoder_->is_progressive() : false);
401         }
402                         
403         void decode_next_frame()
404         {
405                 for(int n = 0; n < 32 && muxer_->empty(); ++n)
406                 {
407                         if(!muxer_->video_ready())
408                                 muxer_->push_video(video_decoder_ ? (*video_decoder_)() : create_frame());
409                         if(!muxer_->audio_ready())
410                                 muxer_->push_audio(audio_decoder_ ? (*audio_decoder_)() : create_frame());
411                 }
412
413                 graph_->set_text(print());
414         }
415 };
416
417 void describe_producer(core::help_sink& sink, const core::help_repository& repo)
418 {
419         sink.short_description(L"A producer for playing media files supported by FFmpeg.");
420         sink.syntax(L"[clip:string] {[loop:LOOP]} {START,SEEK [start:int]} {LENGTH [start:int]} {FILTER [filter:string]} {CHANNEL_LAYOUT [channel_layout:string]}");
421         sink.para()
422                 ->text(L"The FFmpeg Producer can play all media that FFmpeg can play, which includes many ")
423                 ->text(L"QuickTime video codec such as Animation, PNG, PhotoJPEG, MotionJPEG, as well as ")
424                 ->text(L"H.264, FLV, WMV and several audio codecs as well as uncompressed audio.");
425         sink.definitions()
426                 ->item(L"clip", L"The file without the file extension to play. It should reside under the media folder.")
427                 ->item(L"loop", L"Will cause the media file to loop between start and start + length")
428                 ->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.")
429                 ->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.")
430                 ->item(L"filter", L"If specified, will be used as an FFmpeg video filter.")
431                 ->item(L"channel_layout",
432                                 L"Optionally override the automatically deduced audio channel layout. "
433                                 L"Either a named layout as specified in casparcg.config or in the format [type:string]:[channel_order:string] for a custom layout.");
434         sink.para()->text(L"Examples:");
435         sink.example(L">> PLAY 1-10 folder/clip", L"to play all frames in a clip and stop at the last frame.");
436         sink.example(L">> PLAY 1-10 folder/clip LOOP", L"to loop a clip between the first frame and the last frame.");
437         sink.example(L">> PLAY 1-10 folder/clip LOOP START 10", L"to loop a clip between frame 10 and the last frame.");
438         sink.example(L">> PLAY 1-10 folder/clip LOOP START 10 LENGTH 50", L"to loop a clip between frame 10 and frame 60.");
439         sink.example(L">> PLAY 1-10 folder/clip START 10 LENGTH 50", L"to play frames 10-60 in a clip and stop.");
440         sink.example(L">> PLAY 1-10 folder/clip FILTER yadif=1,-1", L"to deinterlace the video.");
441         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.");
442         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.");
443         sink.para()->text(L"The FFmpeg producer also supports changing some of the settings via CALL:");
444         sink.example(L">> CALL 1-10 LOOP 1");
445         sink.example(L">> CALL 1-10 START 10");
446         sink.example(L">> CALL 1-10 LENGTH 50");
447 }
448
449 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
450 {               
451         auto filename = probe_stem(env::media_folder() + L"/" + params.at(0));
452
453         if(filename.empty())
454                 return core::frame_producer::empty();
455         
456         bool loop                       = contains_param(L"LOOP", params);
457         auto start                      = get_param(L"START", params, get_param(L"SEEK", params, static_cast<uint32_t>(0)));
458         auto length                     = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());
459         auto filter_str         = get_param(L"FILTER", params, L"");
460         auto channel_layout     = get_param(L"CHANNEL_LAYOUT", params, L"");
461
462         return create_destroy_proxy(spl::make_shared_ptr(std::make_shared<ffmpeg_producer>(
463                         dependencies.frame_factory,
464                         dependencies.format_desc,
465                         channel_layout,
466                         filename,
467                         filter_str,
468                         loop,
469                         start,
470                         length)));
471 }
472
473 }}