1 #include "..\..\stdafx.h"
\r
5 #include "../../frame/frame_format.h"
\r
6 #include "../../../common/image/image.h"
\r
7 #include "../../../common/utility/scope_exit.h"
\r
9 #include <tbb/concurrent_queue.h>
\r
11 #include <boost/thread.hpp>
\r
14 #include <system_error>
\r
16 #pragma warning(disable : 4482)
\r
18 namespace caspar{ namespace ffmpeg{
\r
20 struct input::implementation : boost::noncopyable
\r
22 implementation(const frame_format_desc& format_desc)
\r
23 : video_frame_rate_(25.0), video_s_index_(-1), audio_s_index_(-1), video_codec_(nullptr), audio_codec_a(nullptr), format_desc_(format_desc)
\r
26 video_packet_buffer_.set_capacity(28);
\r
27 audio_packet_buffer_.set_capacity(28);
\r
32 is_running_ = false;
\r
33 audio_packet_buffer_.clear();
\r
34 video_packet_buffer_.clear();
\r
38 void load(const std::string& filename)
\r
43 AVFormatContext* weak_format_context;
\r
44 if((errn = -av_open_input_file(&weak_format_context, filename.c_str(), nullptr, 0, nullptr)) > 0)
\r
45 BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_errno(errn));
\r
46 format_context.reset(weak_format_context, av_close_input_file);
\r
48 if((errn = -av_find_stream_info(format_context.get())) > 0)
\r
49 BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_errno(errn));
\r
51 video_codec_context_ = open_video_stream();
\r
52 if(!video_codec_context_)
\r
53 CASPAR_LOG(info) << "No video stream found.";
\r
55 audio_codex_context = open_audio_stream();
\r
56 if(!audio_codex_context)
\r
57 CASPAR_LOG(info) << "No audio stream found.";
\r
59 if(!video_codec_context_ && !audio_codex_context)
\r
60 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("No video or audio codec found."));
\r
62 video_frame_rate_ = static_cast<double>(video_codec_context_->time_base.den) / static_cast<double>(video_codec_context_->time_base.num);
\r
65 io_thread_ = boost::thread([=]{this->read_file();});
\r
69 video_codec_context_.reset();
\r
70 audio_codex_context.reset();
\r
71 format_context.reset();
\r
72 video_frame_rate_ = 25.0;
\r
73 video_s_index_ = -1;
\r
74 audio_s_index_ = -1;
\r
77 filename_ = filename;
\r
80 std::shared_ptr<AVCodecContext> open_video_stream()
\r
82 bool succeeded = false;
\r
84 CASPAR_SCOPE_EXIT([&]
\r
88 video_s_index_ = -1;
\r
89 video_codec_ = nullptr;
\r
93 AVStream** streams_end = format_context->streams+format_context->nb_streams;
\r
94 AVStream** video_stream = std::find_if(format_context->streams, streams_end,
\r
95 [](AVStream* stream) { return stream != nullptr && stream->codec->codec_type == CODEC_TYPE_VIDEO ;});
\r
97 video_s_index_ = video_stream != streams_end ? (*video_stream)->index : -1;
\r
98 if(video_s_index_ == -1)
\r
101 video_codec_ = avcodec_find_decoder((*video_stream)->codec->codec_id);
\r
102 if(video_codec_ == nullptr)
\r
105 if((-avcodec_open((*video_stream)->codec, video_codec_)) > 0)
\r
110 return std::shared_ptr<AVCodecContext>((*video_stream)->codec, avcodec_close);
\r
113 std::shared_ptr<AVCodecContext> open_audio_stream()
\r
115 bool succeeded = false;
\r
117 CASPAR_SCOPE_EXIT([&]
\r
121 audio_s_index_ = -1;
\r
122 audio_codec_a = nullptr;
\r
126 AVStream** streams_end = format_context->streams+format_context->nb_streams;
\r
127 AVStream** audio_stream = std::find_if(format_context->streams, streams_end,
\r
128 [](AVStream* stream) { return stream != nullptr && stream->codec->codec_type == CODEC_TYPE_AUDIO;});
\r
130 audio_s_index_ = audio_stream != streams_end ? (*audio_stream)->index : -1;
\r
131 if(audio_s_index_ == -1)
\r
134 audio_codec_a = avcodec_find_decoder((*audio_stream)->codec->codec_id);
\r
135 if(audio_codec_a == nullptr)
\r
138 if((-avcodec_open((*audio_stream)->codec, audio_codec_a)) > 0)
\r
143 return std::shared_ptr<AVCodecContext>((*audio_stream)->codec, avcodec_close);
\r
148 CASPAR_LOG(info) << "Started ffmpeg_producer::read_file Thread for " << filename_.c_str();
\r
149 win32_exception::install_handler();
\r
153 AVPacket tmp_packet;
\r
156 std::shared_ptr<AVPacket> packet(&tmp_packet, av_free_packet);
\r
158 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
160 if(packet->stream_index == video_s_index_)
\r
161 video_packet_buffer_.push(std::make_shared<video_packet>(packet, nullptr, format_desc_, video_codec_context_.get(), video_codec_)); // NOTE: video_packet makes a copy of AVPacket
\r
162 else if(packet->stream_index == audio_s_index_)
\r
163 audio_packet_buffer_.push(std::make_shared<audio_packet>(packet, audio_codex_context.get(), audio_codec_a, video_frame_rate_));
\r
165 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
166 is_running_ = false;
\r
171 CASPAR_LOG_CURRENT_EXCEPTION();
\r
174 is_running_ = false;
\r
176 CASPAR_LOG(info) << " Ended ffmpeg_producer::read_file Thread for " << filename_.c_str();
\r
179 video_packet_ptr get_video_packet()
\r
181 video_packet_ptr video_packet;
\r
182 video_packet_buffer_.try_pop(video_packet);
\r
183 return video_packet;
\r
186 audio_packet_ptr get_audio_packet()
\r
188 audio_packet_ptr audio_packet;
\r
189 audio_packet_buffer_.try_pop(audio_packet);
\r
190 return audio_packet;
\r
193 bool is_eof() const
\r
195 return !is_running_ && video_packet_buffer_.empty() && audio_packet_buffer_.empty();
\r
198 std::string filename_;
\r
199 std::shared_ptr<AVFormatContext> format_context; // Destroy this last
\r
201 std::shared_ptr<AVCodecContext> video_codec_context_;
\r
202 AVCodec* video_codec_;
\r
204 std::shared_ptr<AVCodecContext> audio_codex_context;
\r
205 AVCodec* audio_codec_a;
\r
207 tbb::atomic<bool> loop_;
\r
208 int video_s_index_;
\r
209 int audio_s_index_;
\r
211 frame_format_desc format_desc_;
\r
213 tbb::concurrent_bounded_queue<video_packet_ptr> video_packet_buffer_;
\r
214 tbb::concurrent_bounded_queue<audio_packet_ptr> audio_packet_buffer_;
\r
215 boost::thread io_thread_;
\r
216 tbb::atomic<bool> is_running_;
\r
218 double video_frame_rate_;
\r
221 input::input(const frame_format_desc& format_desc) : impl_(new implementation(format_desc)){}
\r
222 void input::load(const std::string& filename){impl_->load(filename);}
\r
223 void input::set_loop(bool value){impl_->loop_ = value;}
\r
224 const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_codec_context_;}
\r
225 const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_codex_context;}
\r
226 bool input::is_eof() const{return impl_->is_eof();}
\r
227 video_packet_ptr input::get_video_packet(){return impl_->get_video_packet();}
\r
228 audio_packet_ptr input::get_audio_packet(){return impl_->get_audio_packet();}
\r