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