1 #include "..\..\stdafx.h"
\r
5 #include "../../format/video_format.h"
\r
6 #include "../../../common/utility/memory.h"
\r
7 #include "../../../common/utility/scope_exit.h"
\r
9 #include <tbb/concurrent_queue.h>
\r
10 #include <tbb/queuing_mutex.h>
\r
12 #include <boost/thread.hpp>
\r
15 #include <system_error>
\r
17 #if defined(_MSC_VER)
\r
18 #pragma warning (push)
\r
19 #pragma warning (disable : 4244)
\r
23 #define __STDC_CONSTANT_MACROS
\r
24 #define __STDC_LIMIT_MACROS
\r
25 #include <libavformat/avformat.h>
\r
27 #if defined(_MSC_VER)
\r
28 #pragma warning (pop)
\r
31 namespace caspar { namespace core { namespace ffmpeg{
\r
33 struct input::implementation : boost::noncopyable
\r
35 implementation() : video_s_index_(-1), audio_s_index_(-1), video_codec_(nullptr), audio_codec_(nullptr)
\r
38 //file_buffer_size_ = 0;
\r
39 video_packet_buffer_.set_capacity(50);
\r
40 audio_packet_buffer_.set_capacity(50);
\r
50 is_running_ = false;
\r
51 audio_packet_buffer_.clear();
\r
52 video_packet_buffer_.clear();
\r
53 //file_buffer_size_ = 0;
\r
54 //file_buffer_size_cond_.notify_all();
\r
58 void load(const std::string& filename)
\r
63 AVFormatContext* weak_format_context_;
\r
64 if((errn = -av_open_input_file(&weak_format_context_, filename.c_str(), nullptr, 0, nullptr)) > 0)
\r
65 BOOST_THROW_EXCEPTION(file_read_error() << msg_info("No video or audio codec found."));
\r
66 format_context_.reset(weak_format_context_, av_close_input_file);
\r
68 if((errn = -av_find_stream_info(format_context_.get())) > 0)
\r
69 throw std::runtime_error("File read error");
\r
71 video_codec_context_ = open_video_stream();
\r
72 if(!video_codec_context_)
\r
73 CASPAR_LOG(warning) << "No video stream found.";
\r
75 audio_codex_context_ = open_audio_stream();
\r
76 if(!audio_codex_context_)
\r
77 CASPAR_LOG(warning) << "No audio stream found.";
\r
79 if(!video_codec_context_ && !audio_codex_context_)
\r
80 BOOST_THROW_EXCEPTION(file_read_error() << msg_info("No video or audio codec found."));
\r
84 video_codec_context_.reset();
\r
85 audio_codex_context_.reset();
\r
86 format_context_.reset();
\r
87 video_s_index_ = -1;
\r
88 audio_s_index_ = -1;
\r
91 filename_ = filename;
\r
96 io_thread_ = boost::thread([=]{read_file();});
\r
99 std::shared_ptr<AVCodecContext> open_video_stream()
\r
101 AVStream** streams_end = format_context_->streams+format_context_->nb_streams;
\r
102 AVStream** video_stream = std::find_if(format_context_->streams, streams_end,
\r
103 [](AVStream* stream) { return stream != nullptr && stream->codec->codec_type == CODEC_TYPE_VIDEO ;});
\r
105 video_s_index_ = video_stream != streams_end ? (*video_stream)->index : -1;
\r
106 if(video_s_index_ == -1)
\r
109 video_codec_ = avcodec_find_decoder((*video_stream)->codec->codec_id);
\r
110 if(video_codec_ == nullptr)
\r
113 if((-avcodec_open((*video_stream)->codec, video_codec_)) > 0)
\r
116 return std::shared_ptr<AVCodecContext>((*video_stream)->codec, avcodec_close);
\r
119 std::shared_ptr<AVCodecContext> open_audio_stream()
\r
121 AVStream** streams_end = format_context_->streams+format_context_->nb_streams;
\r
122 AVStream** audio_stream = std::find_if(format_context_->streams, streams_end,
\r
123 [](AVStream* stream) { return stream != nullptr && stream->codec->codec_type == CODEC_TYPE_AUDIO;});
\r
125 audio_s_index_ = audio_stream != streams_end ? (*audio_stream)->index : -1;
\r
126 if(audio_s_index_ == -1)
\r
129 audio_codec_ = avcodec_find_decoder((*audio_stream)->codec->codec_id);
\r
130 if(audio_codec_ == nullptr)
\r
133 if((-avcodec_open((*audio_stream)->codec, audio_codec_)) > 0)
\r
136 return std::shared_ptr<AVCodecContext>((*audio_stream)->codec, avcodec_close);
\r
141 CASPAR_LOG(info) << "Started ffmpeg_producer::read_file Thread for " << filename_.c_str();
\r
142 win32_exception::install_handler();
\r
144 is_running_ = true;
\r
145 AVPacket tmp_packet;
\r
148 std::shared_ptr<AVPacket> packet(&tmp_packet, av_free_packet);
\r
149 tbb::queuing_mutex::scoped_lock lock(seek_mutex_);
\r
151 if (av_read_frame(format_context_.get(), packet.get()) >= 0) // NOTE: Packet is only valid until next call of av_read_frame or av_close_input_file
\r
153 if(packet->stream_index == video_s_index_)
\r
155 video_packet_buffer_.push(std::make_shared<aligned_buffer>(packet->data, packet->data + packet->size));
\r
156 packet_wait_cond_.notify_all();
\r
157 //file_buffer_size_ += packet->size;
\r
159 else if(packet->stream_index == audio_s_index_)
\r
161 audio_packet_buffer_.push(std::make_shared<aligned_buffer>(packet->data, packet->data + packet->size));
\r
162 packet_wait_cond_.notify_all();
\r
163 //file_buffer_size_ += packet->size;
\r
166 else if(!loop_ || av_seek_frame(format_context_.get(), -1, 0, AVSEEK_FLAG_BACKWARD) < 0) // TODO: av_seek_frame does not work for all formats
\r
167 is_running_ = false;
\r
171 // boost::unique_lock<boost::mutex> lock(file_buffer_size_mutex_);
\r
172 // while(file_buffer_size_ > 32*1000000)
\r
173 // file_buffer_size_cond_.wait(lock);
\r
177 is_running_ = false;
\r
179 CASPAR_LOG(info) << " Ended ffmpeg_producer::read_file Thread for " << filename_.c_str();
\r
182 aligned_buffer get_video_packet()
\r
184 std::shared_ptr<aligned_buffer> video_packet;
\r
185 if(video_packet_buffer_.try_pop(video_packet))
\r
187 return std::move(*video_packet);
\r
188 //file_buffer_size_ -= video_packet->size;
\r
189 //file_buffer_size_cond_.notify_all();
\r
191 return aligned_buffer();
\r
194 aligned_buffer get_audio_packet()
\r
196 std::shared_ptr<aligned_buffer> audio_packet;
\r
197 if(audio_packet_buffer_.try_pop(audio_packet))
\r
199 return std::move(*audio_packet);
\r
200 //file_buffer_size_ -= audio_packet->size;
\r
201 //file_buffer_size_cond_.notify_all();
\r
203 return aligned_buffer();
\r
206 bool is_eof() const
\r
208 return !is_running_ && video_packet_buffer_.empty() && audio_packet_buffer_.empty();
\r
211 void wait_for_packet()
\r
213 boost::unique_lock<boost::mutex> lock(packet_wait_mutex_);
\r
214 while(is_running_ && video_packet_buffer_.empty() && audio_packet_buffer_.empty())
\r
215 packet_wait_cond_.wait(lock);
\r
218 bool seek(unsigned long long seek_target)
\r
220 tbb::queuing_mutex::scoped_lock lock(seek_mutex_);
\r
221 if(av_seek_frame(format_context_.get(), -1, seek_target*AV_TIME_BASE, 0) >= 0)
\r
223 video_packet_buffer_.clear();
\r
224 audio_packet_buffer_.clear();
\r
225 // TODO: Not sure its enough to jsut flush in input class
\r
226 if(video_codec_context_)
\r
227 avcodec_flush_buffers(video_codec_context_.get());
\r
228 if(audio_codex_context_)
\r
229 avcodec_flush_buffers(audio_codex_context_.get());
\r
236 //int file_buffer_max_size_;
\r
237 //tbb::atomic<int> file_buffer_size_;
\r
238 //boost::condition_variable file_buffer_size_cond_;
\r
239 //boost::mutex file_buffer_size_mutex_;
\r
241 boost::condition_variable packet_wait_cond_;
\r
242 boost::mutex packet_wait_mutex_;
\r
244 std::shared_ptr<AVFormatContext> format_context_; // Destroy this last
\r
246 tbb::queuing_mutex seek_mutex_;
\r
248 std::string filename_;
\r
250 std::shared_ptr<AVCodecContext> video_codec_context_;
\r
251 AVCodec* video_codec_;
\r
253 std::shared_ptr<AVCodecContext> audio_codex_context_;
\r
254 AVCodec* audio_codec_;
\r
256 tbb::atomic<bool> loop_;
\r
257 int video_s_index_;
\r
258 int audio_s_index_;
\r
260 tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> video_packet_buffer_;
\r
261 tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> audio_packet_buffer_;
\r
262 boost::thread io_thread_;
\r
263 tbb::atomic<bool> is_running_;
\r
266 input::input() : impl_(new implementation()){}
\r
267 void input::load(const std::string& filename){impl_->load(filename);}
\r
268 void input::set_loop(bool value){impl_->loop_ = value;}
\r
269 const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_codec_context_;}
\r
270 const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_codex_context_;}
\r
271 bool input::is_eof() const{return impl_->is_eof();}
\r
272 aligned_buffer input::get_video_packet(){return impl_->get_video_packet();}
\r
273 aligned_buffer input::get_audio_packet(){return impl_->get_audio_packet();}
\r
274 bool input::seek(unsigned long long frame){return impl_->seek(frame);}
\r
275 void input::start(){impl_->start();}
\r
276 void input::wait_for_packet(){impl_->wait_for_packet();}
\r