\r
std::vector<const producer_factory_t> g_factories;\r
\r
-const safe_ptr<frame_producer>& frame_producer::empty() // nothrow\r
+class last_frame_producer : public frame_producer\r
{\r
- struct empty_frame_producer : public frame_producer\r
+ const std::wstring print_;\r
+ const safe_ptr<basic_frame> frame_;\r
+ const int64_t nb_frames_;\r
+public:\r
+ last_frame_producer(const safe_ptr<frame_producer>& producer) \r
+ : print_(producer->print())\r
+ , frame_(producer->last_frame() != basic_frame::eof() ? producer->last_frame() : basic_frame::empty())\r
+ , nb_frames_(producer->nb_frames())\r
{\r
- virtual safe_ptr<basic_frame> receive(int){return basic_frame::empty();}\r
- virtual safe_ptr<basic_frame> last_frame() const{return basic_frame::empty();}\r
- virtual void set_frame_factory(const safe_ptr<frame_factory>&){}\r
- virtual int64_t nb_frames() const {return 0;}\r
- virtual std::wstring print() const { return L"empty";}\r
- };\r
+ }\r
+ \r
+ virtual safe_ptr<basic_frame> receive(int){return frame_;}\r
+ virtual safe_ptr<core::basic_frame> last_frame() const{return frame_;}\r
+ virtual std::wstring print() const{return L"dummy[" + print_ + L"]";}\r
+ virtual int64_t nb_frames() const {return nb_frames_;} \r
+};\r
+\r
+struct empty_frame_producer : public frame_producer\r
+{\r
+ virtual safe_ptr<basic_frame> receive(int){return basic_frame::empty();}\r
+ virtual safe_ptr<basic_frame> last_frame() const{return basic_frame::empty();}\r
+ virtual void set_frame_factory(const safe_ptr<frame_factory>&){}\r
+ virtual int64_t nb_frames() const {return 0;}\r
+ virtual std::wstring print() const { return L"empty";}\r
+};\r
+\r
+const safe_ptr<frame_producer>& frame_producer::empty() // nothrow\r
+{\r
static safe_ptr<frame_producer> producer = make_safe<empty_frame_producer>();\r
return producer;\r
} \r
{\r
CASPAR_LOG(info) << producer->print() << " End Of File.";\r
auto following = producer->get_following_producer();\r
- following->set_leading_producer(producer);\r
- producer = std::move(following); \r
- \r
- if(producer == frame_producer::empty())\r
- return basic_frame::eof();\r
+ if(following != frame_producer::empty())\r
+ {\r
+ following->set_leading_producer(producer);\r
+ producer = std::move(following);\r
+ }\r
+ else\r
+ producer = make_safe<last_frame_producer>(producer);\r
\r
return receive_and_follow(producer, hints);\r
}\r
{ \r
safe_ptr<frame_producer> foreground_;\r
safe_ptr<frame_producer> background_;\r
- bool is_paused_;\r
- int auto_play_delta_;\r
int64_t frame_number_;\r
- safe_ptr<core::basic_frame> last_frame_;\r
+ int auto_play_delta_;\r
+ bool is_paused_;\r
\r
public:\r
implementation() \r
: foreground_(frame_producer::empty())\r
, background_(frame_producer::empty())\r
- , is_paused_(false)\r
- , auto_play_delta_(-1)\r
, frame_number_(0)\r
- , last_frame_(core::basic_frame::empty()){}\r
+ , auto_play_delta_(std::numeric_limits<int>::min())\r
+ , is_paused_(false)\r
+ {\r
+ }\r
\r
void pause()\r
{\r
if(background_ != frame_producer::empty())\r
{\r
background_->set_leading_producer(foreground_);\r
- foreground_ = background_;\r
- frame_number_ = 0;\r
- auto_play_delta_ = -1;\r
- background_ = frame_producer::empty();\r
+ \r
+ foreground_ = background_;\r
+ background_ = frame_producer::empty();\r
+ frame_number_ = 0;\r
+ auto_play_delta_ = std::numeric_limits<int>::min(); \r
}\r
- resume();\r
+\r
+ is_paused_ = false;\r
}\r
\r
void stop()\r
{\r
- foreground_ = frame_producer::empty();\r
- frame_number_ = 0;\r
- last_frame_ = core::basic_frame::empty();\r
+ foreground_ = frame_producer::empty();\r
+ background_ = background_;\r
+ frame_number_ = 0;\r
+ auto_play_delta_ = std::numeric_limits<int>::min();\r
+\r
+ is_paused_ = true;\r
}\r
\r
safe_ptr<basic_frame> receive()\r
try\r
{\r
if(is_paused_)\r
- return disable_audio(last_frame_);\r
+ return disable_audio(foreground_->last_frame());\r
\r
- const auto frames_left = foreground_->nb_frames() - (++frame_number_) - auto_play_delta_;\r
-\r
auto frame = receive_and_follow(foreground_, frame_producer::NO_HINT);\r
if(frame == core::basic_frame::late())\r
return foreground_->last_frame();\r
- \r
- if(auto_play_delta_ >= 0)\r
- {\r
- CASPAR_ASSERT(background_ != core::frame_producer::empty());\r
- if(frames_left <= 0 || frame == core::basic_frame::eof())\r
- {\r
- //CASPAR_ASSERT(frame != core::basic_frame::eof() && "Received early EOF. Media duration metadata incorrect.");\r
-\r
- CASPAR_LOG(info) << L"Automatically playing next clip with " << auto_play_delta_ << " frames offset. Frames left: " << frames_left;\r
- \r
- play();\r
- frame = receive();\r
- }\r
- }\r
\r
- if(frame == core::basic_frame::eof())\r
+ auto frames_left = foreground_->nb_frames() - (++frame_number_) - auto_play_delta_;\r
+ if(frames_left < 1)\r
{\r
- pause();\r
+ play();\r
return receive();\r
}\r
\r
- return last_frame_ = frame;\r
+ return frame;\r
}\r
catch(...)\r
{\r
double fps_;\r
frame_muxer muxer_;\r
\r
- int late_frames_;\r
const int start_;\r
const bool loop_;\r
const size_t length_;\r
, audio_decoder_(input_.context(), frame_factory->get_video_format_desc())\r
, fps_(video_decoder_.fps())\r
, muxer_(fps_, frame_factory)\r
- , late_frames_(0)\r
, start_(start)\r
, loop_(loop)\r
, length_(length)\r
{\r
if(input_.eof())\r
return core::basic_frame::eof();\r
- else\r
- {\r
+ else \r
graph_->add_tag("underflow"); \r
- ++late_frames_; \r
- }\r
}\r
\r
return frame;\r
\r
// TODO: Might need to scale nb_frames av frame_muxer transformations.\r
\r
- return nb_frames + late_frames_ - start_;\r
+ return nb_frames - start_;\r
}\r
\r
virtual std::wstring print() const\r