\r
#include "input.h"\r
\r
-#include "../../format/video_format.h"\r
+#include "../../video_format.h"\r
\r
#include <common/concurrency/executor.h>\r
+#include <common/diagnostics/graph.h>\r
\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/queuing_mutex.h>\r
#pragma warning (disable : 4244)\r
#endif\r
\r
-\r
extern "C" \r
{\r
#define __STDC_CONSTANT_MACROS\r
namespace caspar { namespace core { namespace ffmpeg{\r
\r
struct input::implementation : boost::noncopyable\r
-{\r
- static const size_t BUFFER_SIZE = 2 << 25;\r
+{ \r
+ static const size_t BUFFER_SIZE = 2 << 24;\r
\r
- implementation(const std::wstring& filename) : video_s_index_(-1), audio_s_index_(-1), filename_(filename)\r
- { \r
- static boost::once_flag av_register_all_flag = BOOST_ONCE_INIT;\r
- boost::call_once(av_register_all, av_register_all_flag); \r
- \r
- static boost::once_flag avcodec_init_flag = BOOST_ONCE_INIT;\r
- boost::call_once(avcodec_init, avcodec_init_flag); \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
+ const std::wstring filename_;\r
+\r
+ bool loop_;\r
+ int video_s_index_;\r
+ int audio_s_index_;\r
+\r
+ tbb::atomic<size_t> buffer_size_;\r
+ \r
+ tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> video_packet_buffer_;\r
+ tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> audio_packet_buffer_;\r
+ \r
+ executor executor_;\r
+public:\r
+ explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop) \r
+ : graph_(graph)\r
+ , loop_(loop)\r
+ , video_s_index_(-1)\r
+ , audio_s_index_(-1)\r
+ , filename_(filename)\r
+ { \r
+ graph_->set_color("input_buffer_size", diagnostics::color(0.0f, 1.0f, 1.0f)); \r
\r
- loop_ = false; \r
- \r
int errn;\r
AVFormatContext* weak_format_context_;\r
if((errn = -av_open_input_file(&weak_format_context_, narrow(filename).c_str(), nullptr, 0, nullptr)) > 0)\r
\r
if(!video_codec_context_ && !audio_codex_context_)\r
BOOST_THROW_EXCEPTION(file_read_error() << msg_info("No video or audio codec context found.")); \r
- \r
+ \r
executor_.start();\r
executor_.begin_invoke([this]{read_file();});\r
CASPAR_LOG(info) << print() << " started.";\r
\r
~implementation()\r
{\r
+ executor_.clear();\r
CASPAR_LOG(info) << print() << " ended.";\r
}\r
\r
{\r
AVPacket tmp_packet;\r
safe_ptr<AVPacket> read_packet(&tmp_packet, av_free_packet); \r
- tbb::queuing_mutex::scoped_lock lock(seek_mutex_); \r
\r
if (av_read_frame(format_context_.get(), read_packet.get()) >= 0) // NOTE: read_packet is only valid until next call of av_safe_ptr<read_frame> or av_close_input_file\r
{\r
if(read_packet->stream_index == video_s_index_) \r
{\r
buffer_size_ += packet->size();\r
- video_packet_buffer_.try_push(std::move(packet)); \r
+ video_packet_buffer_.try_push(std::move(packet)); \r
}\r
else if(read_packet->stream_index == audio_s_index_) \r
{\r
audio_packet_buffer_.try_push(std::move(packet));\r
}\r
}\r
- 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
- executor_.stop(executor::no_wait);\r
+ else if(!loop_ || !seek_frame(0, AVSEEK_FLAG_BACKWARD)) // TODO: av_seek_frame does not work for all formats\r
+ executor_.stop();\r
+ graph_->update("input_buffer_size", static_cast<float>(buffer_size_)/static_cast<float>(BUFFER_SIZE));\r
}\r
}\r
+ \r
+ bool seek_frame(int64_t seek_target, int flags = 0)\r
+ { \r
+ static const AVRational TIME_BASE_Q = {1, AV_TIME_BASE};\r
+ \r
+ int stream_index = std::max(video_s_index_, audio_s_index_);\r
+ seek_target *= AV_TIME_BASE;\r
+\r
+ if(stream_index >= 0) \r
+ seek_target = av_rescale_q(seek_target, TIME_BASE_Q, format_context_->streams[stream_index]->time_base);\r
+ \r
+ bool result = av_seek_frame(format_context_.get(), stream_index, seek_target, flags) >= 0;\r
+ if(!result)\r
+ CASPAR_LOG(warning) << "Failed to seek frame.";\r
+ return result;\r
+ }\r
\r
aligned_buffer get_video_packet()\r
{\r
return !executor_.is_running() && video_packet_buffer_.empty() && audio_packet_buffer_.empty();\r
}\r
\r
- // TODO: Not properly done.\r
- bool seek(unsigned long long seek_target)\r
- {\r
- tbb::queuing_mutex::scoped_lock lock(seek_mutex_);\r
- if(av_seek_frame(format_context_.get(), -1, seek_target*AV_TIME_BASE, 0) < 0)\r
- return false;\r
-\r
- return true;\r
- }\r
-\r
double fps() const\r
{\r
return static_cast<double>(video_codec_context_->time_base.den) / static_cast<double>(video_codec_context_->time_base.num);\r
{\r
return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"] Buffer thread";\r
}\r
- \r
- std::shared_ptr<AVFormatContext> format_context_; // Destroy this last\r
-\r
- tbb::queuing_mutex seek_mutex_;\r
-\r
- const std::wstring filename_;\r
-\r
- std::shared_ptr<AVCodecContext> video_codec_context_;\r
-\r
- std::shared_ptr<AVCodecContext> audio_codex_context_;\r
-\r
- tbb::atomic<bool> loop_;\r
- int video_s_index_;\r
- int audio_s_index_;\r
- \r
- tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> video_packet_buffer_;\r
- tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> audio_packet_buffer_;\r
- \r
- tbb::atomic<size_t> buffer_size_;\r
-\r
- executor executor_;\r
};\r
\r
-input::input(const std::wstring& filename) : impl_(new implementation(filename)){}\r
-void input::set_loop(bool value){impl_->loop_ = value;}\r
+input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop) : impl_(new implementation(graph, filename, loop)){}\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
bool input::is_eof() const{return impl_->is_eof();}\r
aligned_buffer input::get_video_packet(){return impl_->get_video_packet();}\r
aligned_buffer input::get_audio_packet(){return impl_->get_audio_packet();}\r
-bool input::seek(unsigned long long frame){return impl_->seek(frame);}\r
double input::fps() const { return impl_->fps(); }\r
}}}
\ No newline at end of file