}\r
\r
namespace caspar {\r
+ \r
+static const size_t PACKET_BUFFER_COUNT = 100; // Assume that av_read_frame distance between audio and video packets is less than PACKET_BUFFER_COUNT.\r
+\r
+class stream\r
+{\r
+ std::shared_ptr<AVCodecContext> ctx_;\r
+ int index_;\r
+ tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> buffer_;\r
+\r
+public:\r
+\r
+ stream()\r
+ : index_(-1)\r
+ {\r
+ buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
+ }\r
+\r
+ int open(std::shared_ptr<AVFormatContext>& fctx, AVMediaType media_type)\r
+ { \r
+ const auto streams = boost::iterator_range<AVStream**>(fctx->streams, fctx->streams+fctx->nb_streams);\r
+ const auto stream = boost::find_if(streams, [&](AVStream* stream) \r
+ {\r
+ return stream && stream->codec->codec_type == media_type;\r
+ });\r
+ \r
+ if(stream == streams.end()) \r
+ return AVERROR_STREAM_NOT_FOUND;\r
+ \r
+ auto codec = avcodec_find_decoder((*stream)->codec->codec_id); \r
+ if(!codec)\r
+ return AVERROR_DECODER_NOT_FOUND;\r
+ \r
+ index_ = (*stream)->index;\r
+\r
+ int errn = tbb_avcodec_open((*stream)->codec, codec);\r
+ if(errn >= 0)\r
+ {\r
+ ctx_.reset((*stream)->codec, tbb_avcodec_close);\r
+\r
+ // Some files give an invalid time_base numerator, try to fix it.\r
+ if(ctx_ && ctx_->time_base.num == 1)\r
+ ctx_->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(ctx_->time_base.den)))-1));\r
+ }\r
+ return errn; \r
+ }\r
+\r
+ std::shared_ptr<AVPacket> pop()\r
+ {\r
+ std::shared_ptr<AVPacket> pkt;\r
+ buffer_.try_pop(pkt);\r
+ return pkt;\r
+ }\r
+\r
+ void push(const std::shared_ptr<AVPacket>& pkt)\r
+ {\r
+ if(pkt->stream_index != index_)\r
+ return;\r
+\r
+ av_dup_packet(pkt.get());\r
+ buffer_.push(pkt); \r
+ }\r
+\r
+ const std::shared_ptr<AVCodecContext>& ctx() { return ctx_; }\r
+\r
+ operator bool(){return ctx_ != nullptr;}\r
+\r
+ double fps() const { return !ctx_ ? -1.0 : static_cast<double>(ctx_->time_base.den) / static_cast<double>(ctx_->time_base.num); }\r
+\r
+ bool empty() const { return buffer_.empty();}\r
+ int size() const { return buffer_.size();}\r
+};\r
\r
struct input::implementation : boost::noncopyable\r
{ \r
- static const size_t PACKET_BUFFER_COUNT = 100; // Assume that av_read_frame distance between audio and video packets is less than PACKET_BUFFER_COUNT.\r
-\r
safe_ptr<diagnostics::graph> graph_;\r
\r
std::shared_ptr<AVFormatContext> format_context_; // Destroy this last\r
-\r
- std::shared_ptr<AVCodecContext> video_codec_context_;\r
- std::shared_ptr<AVCodecContext> audio_codex_context_;\r
- \r
+ \r
const std::wstring filename_;\r
const bool loop_;\r
- int video_s_index_;\r
- int audio_s_index_;\r
- const int start_;\r
- \r
- tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> video_packet_buffer_;\r
- tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> audio_packet_buffer_;\r
+ const int start_; \r
+ double fps_;\r
+\r
+ stream video_stream_;\r
+ stream audio_stream_;\r
\r
std::exception_ptr exception_;\r
executor executor_;\r
explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start) \r
: graph_(graph)\r
, loop_(loop)\r
- , video_s_index_(-1)\r
- , audio_s_index_(-1)\r
, filename_(filename)\r
, executor_(print())\r
, start_(std::max(start, 0))\r
boost::errinfo_errno(AVUNERROR(errn)));\r
}\r
\r
- errn = open_stream(video_codec_context_, AVMEDIA_TYPE_VIDEO, video_s_index_);\r
- if(errn < 0 || !video_codec_context_)\r
+ errn = video_stream_.open(format_context_, AVMEDIA_TYPE_VIDEO);\r
+ if(errn < 0)\r
CASPAR_LOG(warning) << print() << L" Could not open video stream: " << widen(av_error_str(errn));\r
\r
- errn = open_stream(audio_codex_context_, AVMEDIA_TYPE_AUDIO, audio_s_index_);\r
- if(errn < 0 || !audio_codex_context_)\r
+ errn = audio_stream_.open(format_context_, AVMEDIA_TYPE_AUDIO);\r
+ if(errn < 0)\r
CASPAR_LOG(warning) << print() << L" Could not open audio stream: " << widen(av_error_str(errn));\r
\r
- if(!video_codec_context_ && !audio_codex_context_)\r
+ if(!video_stream_ && !audio_stream_)\r
{ \r
BOOST_THROW_EXCEPTION(\r
file_read_error() << \r
msg_info("No video or audio codec context found.")); \r
}\r
\r
- video_packet_buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
- audio_packet_buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
- \r
+ fps_ = video_stream_ ? video_stream_.fps() : audio_stream_.fps();\r
+\r
if(start_ != 0) \r
seek_frame(start_);\r
\r
\r
std::shared_ptr<AVPacket> get_video_packet()\r
{\r
- return get_packet(video_packet_buffer_);\r
+ return video_stream_.pop();\r
}\r
\r
std::shared_ptr<AVPacket> get_audio_packet()\r
{\r
- return get_packet(audio_packet_buffer_);\r
+ return audio_stream_.pop();\r
}\r
\r
bool has_packet() const\r
{\r
- return !video_packet_buffer_.empty() || !audio_packet_buffer_.empty();\r
+ return !video_stream_.empty() || !audio_stream_.empty();\r
}\r
\r
double fps()\r
{\r
- return static_cast<double>(get_default_context()->time_base.den) / static_cast<double>(get_default_context()->time_base.num);\r
+ return fps_;\r
}\r
\r
private:\r
CASPAR_LOG(info) << print() << " Stopping.";\r
}\r
\r
- int open_stream(std::shared_ptr<AVCodecContext>& ctx, int codec_type, int& s_index) const\r
- { \r
- const auto streams = boost::iterator_range<AVStream**>(format_context_->streams, format_context_->streams+format_context_->nb_streams);\r
- const auto stream = boost::find_if(streams, [&](AVStream* stream) \r
- {\r
- return stream != nullptr && stream->codec->codec_type == codec_type;\r
- });\r
- \r
- if(stream == streams.end()) \r
- return AVERROR_STREAM_NOT_FOUND;\r
- \r
- auto codec = avcodec_find_decoder((*stream)->codec->codec_id); \r
- if(codec == nullptr)\r
- return AVERROR_DECODER_NOT_FOUND;\r
- \r
- s_index = (*stream)->index;\r
-\r
- int errn = tbb_avcodec_open((*stream)->codec, codec);\r
- if(errn >= 0)\r
- {\r
- ctx.reset((*stream)->codec, tbb_avcodec_close);\r
-\r
- // Some files give an invalid time_base numerator, try to fix it.\r
- if(ctx && ctx->time_base.num == 1)\r
- ctx->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(ctx->time_base.den)))-1));\r
- }\r
- return errn; \r
- }\r
- \r
- std::shared_ptr<AVCodecContext>& get_default_context()\r
- {\r
- return video_codec_context_ ? video_codec_context_ : audio_codex_context_;\r
- }\r
- \r
void read_file()\r
{ \r
- if(audio_packet_buffer_.size() > 4 && video_packet_buffer_.size() > 4)\r
- boost::this_thread::sleep(boost::posix_time::millisec(5)); // There are enough packets, no hurry.\r
+ if(audio_stream_.size() > 4 && video_stream_.size() > 4)\r
+ boost::this_thread::yield(); // There are enough packets, no hurry.\r
\r
try\r
{\r
}\r
else\r
{\r
- if(read_packet->stream_index == video_s_index_) \r
- push_packet(video_packet_buffer_, read_packet);\r
- else if(read_packet->stream_index == audio_s_index_) \r
- push_packet(audio_packet_buffer_, read_packet);\r
+ video_stream_.push(read_packet);\r
+ audio_stream_.push(read_packet);\r
}\r
\r
- graph_->update_value("input-buffer", static_cast<float>(video_packet_buffer_.size())/static_cast<float>(PACKET_BUFFER_COUNT)); \r
+ graph_->update_value("input-buffer", static_cast<float>(std::max(video_stream_.size(), audio_stream_.size()))/static_cast<float>(PACKET_BUFFER_COUNT)); \r
}\r
catch(...)\r
{\r
void seek_frame(int64_t frame, int flags = 0)\r
{ \r
// Convert from frames into seconds.\r
- const auto ts = frame*static_cast<int64_t>((AV_TIME_BASE*get_default_context()->time_base.num) / get_default_context()->time_base.den);\r
+ const auto ts = frame*static_cast<int64_t>(AV_TIME_BASE/fps_);\r
\r
const int errn = av_seek_frame(format_context_.get(), -1, ts, flags | AVSEEK_FLAG_FRAME);\r
if(errn < 0)\r
return errn == AVERROR_EOF || errn == AVERROR(EIO); // av_read_frame doesn't always correctly return AVERROR_EOF;\r
}\r
\r
- void push_packet(tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>>& buffer, const std::shared_ptr<AVPacket>& read_packet)\r
- {\r
- av_dup_packet(read_packet.get());\r
- buffer.push(read_packet); \r
- }\r
-\r
- std::shared_ptr<AVPacket> get_packet(tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>>& buffer)\r
- {\r
- std::shared_ptr<AVPacket> packet;\r
- buffer.try_pop(packet);\r
- return packet;\r
- }\r
-\r
std::wstring print() const\r
{\r
return L"ffmpeg_input[" + filename_ + L"]";\r
\r
input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start) \r
: impl_(new implementation(graph, filename, loop, start)){}\r
-const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_codec_context_;}\r
-const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_codex_context_;}\r
+const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_stream_.ctx();}\r
+const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_stream_.ctx();}\r
bool input::has_packet() const{return impl_->has_packet();}\r
bool input::is_running() const {return impl_->executor_.is_running();}\r
std::shared_ptr<AVPacket> input::get_video_packet(){return impl_->get_video_packet();}\r