]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/input/input.cpp
dd6c5cf4c4b613209bc63001c7d020652ac73a95
[casparcg] / modules / ffmpeg / producer / input / input.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 "input.h"
25
26 #include "../util/util.h"
27 #include "../../ffmpeg_error.h"
28
29 #include <common/diagnostics/graph.h>
30 #include <common/executor.h>
31 #include <common/lock.h>
32 #include <common/except.h>
33 #include <common/log.h>
34
35 #include <core/video_format.h>
36
37 #include <tbb/concurrent_queue.h>
38 #include <tbb/atomic.h>
39 #include <tbb/recursive_mutex.h>
40
41 #include <boost/thread/condition_variable.hpp>
42 #include <boost/thread/mutex.hpp>
43 #include <boost/thread/thread.hpp>
44
45 #if defined(_MSC_VER)
46 #pragma warning (push)
47 #pragma warning (disable : 4244)
48 #endif
49 extern "C" 
50 {
51         #define __STDC_CONSTANT_MACROS
52         #define __STDC_LIMIT_MACROS
53         #include <libavformat/avformat.h>
54 }
55 #if defined(_MSC_VER)
56 #pragma warning (pop)
57 #endif
58
59 namespace caspar { namespace ffmpeg {
60
61 static const int MIN_FRAMES = 25;
62
63 class stream
64 {
65         stream(const stream&);
66         stream& operator=(const stream&);
67
68         typedef tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>>::size_type size_type;
69
70         int                                                                                                              index_;
71         tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> packets_;
72 public:
73
74         stream(int index) 
75                 : index_(index)
76         {
77         }
78         
79         void push(const std::shared_ptr<AVPacket>& packet)
80         {
81                 if(packet && packet->data && packet->stream_index != index_)
82                         return;
83
84                 packets_.push(packet);
85         }
86
87         bool try_pop(std::shared_ptr<AVPacket>& packet)
88         {
89                 return packets_.try_pop(packet);
90         }
91
92         void clear()
93         {
94                 std::shared_ptr<AVPacket> packet;
95                 while(packets_.try_pop(packet));
96         }
97                 
98         size_type size() const
99         {
100                 return index_ != -1 ? packets_.size() : std::numeric_limits<size_type>::max();
101         }
102 };
103                 
104 struct input::impl : boost::noncopyable
105 {               
106         const spl::shared_ptr<diagnostics::graph>       graph_;
107
108         const std::wstring                                                      filename_;
109         const spl::shared_ptr<AVFormatContext>          format_context_                 = open_input(filename_); // Destroy this last
110         const int                                                                       default_stream_index_   = av_find_default_stream_index(format_context_.get());
111
112         tbb::atomic<uint32_t>                                           start_;         
113         tbb::atomic<uint32_t>                                           length_;
114         tbb::atomic<bool>                                                       loop_;
115         double                                                                          fps_                                    = read_fps(*format_context_, 0.0);
116         uint32_t                                                                        frame_number_                   = 0;
117
118         stream                                                                          video_stream_                   = av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
119         stream                                                                          audio_stream_                   = av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
120
121         boost::optional<uint32_t>                                       seek_target_;
122
123         tbb::atomic<bool>                                                       is_running_;
124         boost::mutex                                                            mutex_;
125         boost::condition_variable                                       cond_;
126         boost::thread                                                           thread_;
127         
128         impl(const spl::shared_ptr<diagnostics::graph> graph, const std::wstring& filename, const bool loop, const uint32_t start, const uint32_t length) 
129                 : graph_(graph)
130                 , filename_(filename)
131         {               
132                 start_                  = start;
133                 length_                 = length;
134                 loop_                   = loop;
135                 is_running_             = true;
136
137                 if(start_ != 0)
138                         seek_target_ = start_;
139                                                                                                                 
140                 graph_->set_color("seek", diagnostics::color(1.0f, 0.5f, 0.0f));        
141                 graph_->set_color("audio-buffer", diagnostics::color(0.7f, 0.4f, 0.4f));
142                 graph_->set_color("video-buffer", diagnostics::color(1.0f, 1.0f, 0.0f));        
143                 
144                 for(int n = 0; n < 8; ++n)
145                         tick();
146
147                 thread_ = boost::thread([this]{run();});
148         }
149
150         ~impl()
151         {
152                 is_running_ = false;
153                 cond_.notify_one();
154                 thread_.join();
155         }
156         
157         bool try_pop_video(std::shared_ptr<AVPacket>& packet)
158         {                               
159                 bool result = video_stream_.try_pop(packet);
160                 if(result)
161                         cond_.notify_one();
162                 
163                 graph_->set_value("video-buffer", std::min(1.0, static_cast<double>(video_stream_.size()/MIN_FRAMES)));
164                                 
165                 return result;
166         }
167         
168         bool try_pop_audio(std::shared_ptr<AVPacket>& packet)
169         {                               
170                 bool result = audio_stream_.try_pop(packet);
171                 if(result)
172                         cond_.notify_one();
173                                 
174                 graph_->set_value("audio-buffer", std::min(1.0, static_cast<double>(audio_stream_.size()/MIN_FRAMES)));
175
176                 return result;
177         }
178
179         void seek(uint32_t target)
180         {
181                 {
182                         boost::lock_guard<boost::mutex> lock(mutex_);
183
184                         seek_target_ = target;
185                         video_stream_.clear();
186                         audio_stream_.clear();
187                 }
188                 
189                 cond_.notify_one();
190         }
191                 
192         std::wstring print() const
193         {
194                 return L"ffmpeg_input[" + filename_ + L")]";
195         }
196
197 private:
198         void internal_seek(uint32_t target)
199         {
200                 graph_->set_tag("seek");        
201
202                 CASPAR_LOG(debug) << print() << " Seeking: " << target;
203
204                 int flags = AVSEEK_FLAG_FRAME;
205                 if(target == 0)
206                 {
207                         // Fix VP6 seeking
208                         int vid_stream_index = av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
209                         if(vid_stream_index >= 0)
210                         {
211                                 auto codec_id = format_context_->streams[vid_stream_index]->codec->codec_id;
212                                 if(codec_id == CODEC_ID_VP6A || codec_id == CODEC_ID_VP6F || codec_id == CODEC_ID_VP6)
213                                         flags = AVSEEK_FLAG_BYTE;
214                         }
215                 }
216                 
217                 auto stream = format_context_->streams[default_stream_index_];
218                 auto codec  = stream->codec;
219                 auto fixed_target = (target*stream->time_base.den*codec->time_base.num)/(stream->time_base.num*codec->time_base.den)*codec->ticks_per_frame;
220                 
221                 THROW_ON_ERROR2(avformat_seek_file(format_context_.get(), default_stream_index_, std::numeric_limits<int64_t>::min(), fixed_target, fixed_target, 0), print());         
222                 
223                 video_stream_.push(nullptr);
224                 audio_stream_.push(nullptr);
225         }
226
227         void tick()
228         {
229                 if(seek_target_)                                
230                 {
231                         internal_seek(*seek_target_);
232                         seek_target_.reset();
233                 }
234
235                 auto packet = create_packet();
236                 
237                 auto ret = av_read_frame(format_context_.get(), packet.get()); // packet is only valid until next call of av_read_frame. Use av_dup_packet to extend its life.  
238                 
239                 if(is_eof(ret))                                                                                                              
240                 {
241                         video_stream_.push(packet);
242                         audio_stream_.push(packet);
243
244                         if(loop_)                       
245                                 internal_seek(start_);                          
246                 }
247                 else
248                 {               
249                         THROW_ON_ERROR(ret, "av_read_frame", print());
250                                         
251                         THROW_ON_ERROR2(av_dup_packet(packet.get()), print());
252                                 
253                         // Make sure that the packet is correctly deallocated even if size and data is modified during decoding.
254                         const auto size = packet->size;
255                         const auto data = packet->data;
256                         
257                         packet = spl::shared_ptr<AVPacket>(packet.get(), [packet, size, data](AVPacket*)
258                         {
259                                 packet->size = size;
260                                 packet->data = data;                            
261                         });
262                                         
263                         const auto stream_time_base = format_context_->streams[packet->stream_index]->time_base;
264                         const auto packet_frame_number = static_cast<uint32_t>((static_cast<double>(packet->pts * stream_time_base.num)/stream_time_base.den)*fps_);
265
266                         if(packet->stream_index == default_stream_index_)
267                                 frame_number_ = packet_frame_number;
268                                         
269                         if(packet_frame_number >= start_ && packet_frame_number < length_)
270                         {
271                                 video_stream_.push(packet);
272                                 audio_stream_.push(packet);
273                         }
274                 }       
275                                                 
276                 graph_->set_value("video-buffer", std::min(1.0, static_cast<double>(video_stream_.size()/MIN_FRAMES)));
277                 graph_->set_value("audio-buffer", std::min(1.0, static_cast<double>(audio_stream_.size()/MIN_FRAMES)));
278         }
279                         
280         bool full() const
281         {
282                 return video_stream_.size() > MIN_FRAMES && audio_stream_.size() > MIN_FRAMES;
283         }
284
285         void run()
286         {
287                 win32_exception::install_handler();
288
289                 while(is_running_)
290                 {
291                         try
292                         {
293                                 boost::this_thread::sleep(boost::posix_time::milliseconds(1));
294                                 
295                                 {
296                                         boost::unique_lock<boost::mutex> lock(mutex_);
297
298                                         while(full() && !seek_target_ && is_running_)
299                                                 cond_.wait(lock);
300                                         
301                                         tick();
302                                 }
303                         }
304                         catch(...)
305                         {
306                                 CASPAR_LOG_CURRENT_EXCEPTION();
307                                 is_running_ = false;
308                         }
309                 }
310         }
311                         
312         bool is_eof(int ret)
313         {
314                 #pragma warning (disable : 4146)
315                 return ret == AVERROR_EOF || ret == AVERROR(EIO) || frame_number_ >= length_; // av_read_frame doesn't always correctly return AVERROR_EOF;
316         }
317 };
318
319 input::input(const spl::shared_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length) 
320         : impl_(new impl(graph, filename, loop, start, length)){}
321 bool input::try_pop_video(std::shared_ptr<AVPacket>& packet){return impl_->try_pop_video(packet);}
322 bool input::try_pop_audio(std::shared_ptr<AVPacket>& packet){return impl_->try_pop_audio(packet);}
323 AVFormatContext& input::context(){return *impl_->format_context_;}
324 void input::loop(bool value){impl_->loop_ = value;}
325 bool input::loop() const{return impl_->loop_;}
326 void input::seek(uint32_t target){impl_->seek(target);}
327 void input::start(uint32_t value){impl_->start_ = value;}
328 uint32_t input::start() const{return impl_->start_;}
329 void input::length(uint32_t value){impl_->length_ = value;}
330 uint32_t input::length() const{return impl_->length_;}
331 }}