/*\r
-* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
*\r
-* This file is part of CasparCG.\r
+* This file is part of CasparCG (www.casparcg.com).\r
*\r
-* CasparCG is free software: you can redistribute it and/or modify\r
-* it under the terms of the GNU General Public License as published by\r
-* the Free Software Foundation, either version 3 of the License, or\r
-* (at your option) any later version.\r
+* CasparCG is free software: you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation, either version 3 of the License, or\r
+* (at your option) any later version.\r
*\r
-* CasparCG is distributed in the hope that it will be useful,\r
-* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-* GNU General Public License for more details.\r
-\r
-* You should have received a copy of the GNU General Public License\r
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
+* CasparCG is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
*\r
+* Author: Robert Nagy, ronag89@gmail.com\r
*/\r
-#if defined(_MSC_VER)\r
-#pragma warning (disable : 4244)\r
-#endif\r
\r
#include "../../stdafx.h"\r
\r
\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/atomic.h>\r
+#include <tbb/recursive_mutex.h>\r
\r
#include <boost/range/algorithm.hpp>\r
#include <boost/thread/condition_variable.hpp>\r
const int default_stream_index_;\r
\r
const std::wstring filename_;\r
- const bool loop_;\r
- const size_t start_; \r
- const size_t length_;\r
+ tbb::atomic<bool> loop_;\r
+ const uint64_t start_; \r
+ const uint64_t length_;\r
size_t frame_number_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> buffer_;\r
boost::condition_variable buffer_cond_;\r
boost::mutex buffer_mutex_;\r
\r
- tbb::atomic<size_t> nb_frames_;\r
- tbb::atomic<size_t> nb_loops_;\r
-\r
boost::thread thread_;\r
tbb::atomic<bool> is_running_;\r
+ tbb::atomic<bool> is_eof_;\r
\r
-public:\r
- explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, size_t start, size_t length) \r
+ tbb::recursive_mutex mutex_;\r
+\r
+ explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int64_t start, int64_t length) \r
: graph_(graph)\r
, format_context_(open_input(filename)) \r
, default_stream_index_(av_find_default_stream_index(format_context_.get()))\r
- , loop_(loop)\r
, filename_(filename)\r
, start_(start)\r
, length_(length)\r
, frame_number_(0)\r
- { \r
+ { \r
+ is_eof_ = false;\r
+ loop_ = loop;\r
+ buffer_size_ = 0;\r
+\r
if(start_ > 0) \r
- seek_frame(start_);\r
+ do_seek(start_);\r
\r
graph_->set_color("seek", diagnostics::color(1.0f, 0.5f, 0.0f)); \r
graph_->set_color("buffer-count", diagnostics::color(0.7f, 0.4f, 0.4f));\r
\r
is_running_ = true;\r
thread_ = boost::thread([this]{run();});\r
+\r
+ CASPAR_LOG(info) << print() << L" Initialized.";\r
}\r
\r
~implementation()\r
\r
return result;\r
}\r
-\r
- size_t nb_frames() const\r
- {\r
- return nb_frames_;\r
- }\r
-\r
- size_t nb_loops() const\r
- {\r
- return nb_loops_;\r
- }\r
-\r
-private:\r
- \r
+ \r
void run()\r
{ \r
caspar::win32_exception::install_handler();\r
\r
void read_next_packet()\r
{ \r
+ tbb::recursive_mutex::scoped_lock lock(mutex_);\r
+\r
auto packet = create_packet();\r
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. \r
\r
if(is_eof(ret)) \r
{\r
- ++nb_loops_;\r
- frame_number_ = 0;\r
+ frame_number_ = 0;\r
+ is_eof_ = true;\r
\r
if(loop_)\r
{\r
- int flags = AVSEEK_FLAG_BACKWARD;\r
-\r
- int vid_stream_index = av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);\r
- if(vid_stream_index >= 0)\r
- {\r
- auto codec_id = format_context_->streams[vid_stream_index]->codec->codec_id;\r
- if(codec_id == CODEC_ID_VP6A || codec_id == CODEC_ID_VP6F || codec_id == CODEC_ID_VP6)\r
- flags |= AVSEEK_FLAG_BYTE;\r
- }\r
-\r
- seek_frame(start_, flags);\r
+ do_seek(start_);\r
graph_->add_tag("seek"); \r
- CASPAR_LOG(debug) << print() << " Looping."; \r
- } \r
- else\r
- {\r
- is_running_ = false;\r
- CASPAR_LOG(debug) << print() << " Stopping.";\r
- }\r
+ CASPAR_LOG(trace) << print() << " Looping."; \r
+ } \r
}\r
else\r
{ \r
THROW_ON_ERROR(ret, "av_read_frame", print());\r
\r
if(packet->stream_index == default_stream_index_)\r
- {\r
- if(nb_loops_ == 0)\r
- ++nb_frames_;\r
++frame_number_;\r
- }\r
\r
THROW_ON_ERROR2(av_dup_packet(packet.get()), print());\r
\r
// Make sure that the packet is correctly deallocated even if size and data is modified during decoding.\r
auto size = packet->size;\r
auto data = packet->data;\r
-\r
+ \r
packet = safe_ptr<AVPacket>(packet.get(), [packet, size, data](AVPacket*)\r
{\r
packet->size = size;\r
\r
bool full() const\r
{\r
- return is_running_ && (buffer_size_ > MAX_BUFFER_SIZE || buffer_.size() > MAX_BUFFER_COUNT) && buffer_.size() > MIN_BUFFER_COUNT;\r
+ return is_running_ && (is_eof_ || (buffer_size_ > MAX_BUFFER_SIZE || buffer_.size() > MAX_BUFFER_COUNT) && buffer_.size() > MIN_BUFFER_COUNT);\r
}\r
+ \r
+ void do_seek(const int64_t target)\r
+ { \r
+ CASPAR_LOG(debug) << print() << " Seeking: " << target;\r
\r
- void seek_frame(int64_t frame, int flags = 0)\r
- { \r
- if(flags == AVSEEK_FLAG_BACKWARD)\r
+ int flags = AVSEEK_FLAG_FRAME;\r
+ if(target == 0)\r
{\r
// Fix VP6 seeking\r
int vid_stream_index = av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);\r
{\r
auto codec_id = format_context_->streams[vid_stream_index]->codec->codec_id;\r
if(codec_id == CODEC_ID_VP6A || codec_id == CODEC_ID_VP6F || codec_id == CODEC_ID_VP6)\r
- flags |= AVSEEK_FLAG_BYTE;\r
+ flags = AVSEEK_FLAG_BYTE;\r
}\r
}\r
+ \r
+ auto stream = format_context_->streams[default_stream_index_];\r
+ auto codec = stream->codec;\r
+ auto fixed_target = (target*stream->time_base.den*codec->time_base.num)/(stream->time_base.num*codec->time_base.den)*codec->ticks_per_frame;\r
+ \r
+ THROW_ON_ERROR2(avformat_seek_file(format_context_.get(), default_stream_index_, std::numeric_limits<int64_t>::min(), fixed_target, std::numeric_limits<int64_t>::max(), 0), print()); \r
+\r
+ is_eof_ = false;\r
+ buffer_cond_.notify_all();\r
+\r
+ auto flush_packet = create_packet();\r
+ flush_packet->data = nullptr;\r
+ flush_packet->size = 0;\r
+ flush_packet->pos = target;\r
+\r
+ buffer_.push(flush_packet);\r
+ } \r
\r
- THROW_ON_ERROR2(av_seek_frame(format_context_.get(), default_stream_index_, frame, flags), print()); \r
+ void seek(int64_t target)\r
+ {\r
+ tbb::recursive_mutex::scoped_lock lock(mutex_);\r
\r
- buffer_.push(flush_packet());\r
- } \r
+ std::shared_ptr<AVPacket> packet;\r
+ while(try_pop(packet))\r
+ {\r
+ }\r
+\r
+ do_seek(target);\r
+ }\r
\r
bool is_eof(int ret)\r
{\r
if(ret == AVERROR(EIO))\r
- CASPAR_LOG(trace) << print() << " Received EIO, assuming EOF. " << nb_frames_;\r
+ CASPAR_LOG(trace) << print() << " Received EIO, assuming EOF. ";\r
if(ret == AVERROR_EOF)\r
- CASPAR_LOG(debug) << print() << " Received EOF. " << nb_frames_;\r
+ CASPAR_LOG(trace) << print() << " Received EOF. ";\r
\r
return ret == AVERROR_EOF || ret == AVERROR(EIO) || frame_number_ >= length_; // av_read_frame doesn't always correctly return AVERROR_EOF;\r
}\r
}\r
};\r
\r
-input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, size_t start, size_t length) \r
+input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int64_t start, int64_t length) \r
: impl_(new implementation(graph, filename, loop, start, length)){}\r
-bool input::eof() const {return !impl_->is_running_;}\r
+bool input::eof() const {return impl_->is_eof_;}\r
bool input::try_pop(std::shared_ptr<AVPacket>& packet){return impl_->try_pop(packet);}\r
safe_ptr<AVFormatContext> input::context(){return impl_->format_context_;}\r
-size_t input::nb_frames() const {return impl_->nb_frames();}\r
-size_t input::nb_loops() const {return impl_->nb_loops();}\r
+void input::loop(bool value){impl_->loop_ = value;}\r
+bool input::loop() const{return impl_->loop_;}\r
+void input::seek(int64_t target){impl_->seek(target);}\r
}}\r