virtual safe_ptr<frame_producer> get_following_producer() const {return frame_producer::empty();} // nothrow\r
virtual void set_leading_producer(const safe_ptr<frame_producer>&) {} // nothrow\r
\r
- static const safe_ptr<frame_producer>& empty(); // nothrow\r
+ virtual int64_t nb_frames() const {return 0;}\r
+\r
+ virtual safe_ptr<core::basic_frame> last_frame() const {return last_frame_;}\r
\r
- safe_ptr<core::basic_frame> last_frame() const {return last_frame_;}\r
+ static const safe_ptr<frame_producer>& empty(); // nothrow\r
\r
private:\r
friend safe_ptr<basic_frame> receive(const safe_ptr<frame_producer>& producer);\r
\r
#include "frame_producer.h"\r
\r
+#include "frame/basic_frame.h"\r
+\r
+\r
namespace caspar { namespace core {\r
\r
struct layer::implementation\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
public:\r
implementation() \r
: foreground_(frame_producer::empty())\r
, background_(frame_producer::empty())\r
- , is_paused_(false){}\r
+ , is_paused_(false)\r
+ , auto_play_delta_(-1){}\r
\r
void pause(){is_paused_ = true;}\r
void resume(){is_paused_ = false;}\r
\r
- void load(const safe_ptr<frame_producer>& producer, bool preview)\r
+ void load(const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta)\r
{ \r
+ auto_play_delta_ = auto_play_delta;\r
+\r
background_ = producer;\r
\r
if(preview) // Play the first frame and pause.\r
{\r
background_->set_leading_producer(foreground_);\r
foreground_ = background_;\r
+ frame_number_ = 0;\r
background_ = frame_producer::empty();\r
}\r
resume();\r
if(is_paused_)\r
return foreground_->last_frame();\r
\r
- return receive_and_follow_w_last(foreground_);\r
+ auto frame = receive_and_follow(foreground_);\r
+ if(frame == core::basic_frame::late())\r
+ return foreground_->last_frame();\r
+ \r
+ ++frame_number_;\r
+\r
+ if(auto_play_delta_ >= 0)\r
+ {\r
+ const auto frames_left = foreground_->nb_frames() - frame_number_ - auto_play_delta_;\r
+\r
+ if(frames_left <= 0 || frame == core::basic_frame::eof())\r
+ {\r
+ CASPAR_VERIFY(auto_play_delta_ != 0 || frame == core::basic_frame::eof())\r
+\r
+ CASPAR_LOG(info) << L"Automatically playing next clip with " << auto_play_delta_ << " frames offset.";\r
+ \r
+ auto_play_delta_ = -1;\r
+ play();\r
+ frame = receive();\r
+ }\r
+\r
+ }\r
+ \r
+ return frame;\r
}\r
};\r
\r
{ \r
impl_.swap(other.impl_);\r
}\r
-void layer::load(const safe_ptr<frame_producer>& frame_producer, bool preview){return impl_->load(frame_producer, preview);} \r
+void layer::load(const safe_ptr<frame_producer>& frame_producer, bool preview, int auto_play_delta){return impl_->load(frame_producer, preview, auto_play_delta);} \r
void layer::play(){impl_->play();}\r
void layer::pause(){impl_->pause();}\r
void layer::stop(){impl_->stop();}\r
\r
void swap(layer& other); // nothrow \r
\r
- void load(const safe_ptr<frame_producer>& producer, bool preview); // nothrow\r
+ void load(const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta); // nothrow\r
void play(); // nothrow\r
void pause(); // nothrow\r
void stop(); // nothrow\r
return frames;\r
}\r
\r
- void load(int index, const safe_ptr<frame_producer>& producer, bool preview)\r
+ void load(int index, const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta)\r
{\r
channel_.execution().invoke([&]\r
{\r
- layers_[index].load(make_safe<destroy_producer_proxy>(channel_.destruction(), producer), preview);\r
+ layers_[index].load(make_safe<destroy_producer_proxy>(channel_.destruction(), producer), preview, auto_play_delta);\r
});\r
}\r
\r
\r
stage::stage(video_channel_context& video_channel) : impl_(new implementation(video_channel)){}\r
void stage::swap(stage& other){impl_->swap(other);}\r
-void stage::load(int index, const safe_ptr<frame_producer>& producer, bool preview){impl_->load(index, producer, preview);}\r
+void stage::load(int index, const safe_ptr<frame_producer>& producer, bool preview, int auto_play_delta){impl_->load(index, producer, preview, auto_play_delta);}\r
void stage::pause(int index){impl_->pause(index);}\r
void stage::play(int index){impl_->play(index);}\r
void stage::stop(int index){impl_->stop(index);}\r
\r
std::map<int, safe_ptr<basic_frame>> execute();\r
\r
- void load(int index, const safe_ptr<frame_producer>& producer, bool preview = false);\r
+ void load(int index, const safe_ptr<frame_producer>& producer, bool preview = false, int auto_play_delta = -1);\r
void pause(int index);\r
void play(int index);\r
void stop(int index);\r
std::vector<int8_t, tbb::cache_aligned_allocator<int8_t>> buffer2_;\r
std::vector<int16_t, tbb::cache_aligned_allocator<int16_t>> audio_samples_; \r
std::queue<std::shared_ptr<AVPacket>> packets_;\r
+\r
+ int64_t nb_frames_;\r
public:\r
explicit implementation(const std::shared_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc) \r
: format_desc_(format_desc) \r
+ , nb_frames_(0)\r
{ \r
AVCodec* dec;\r
index_ = av_find_best_stream(context.get(), AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);\r
\r
codec_context_.reset(context->streams[index_]->codec, avcodec_close);\r
\r
+ //nb_frames_ = context->streams[index_]->nb_frames;\r
+ //if(nb_frames_ == 0)\r
+ // nb_frames_ = context->streams[index_]->duration * context->streams[index_]->time_base.den;\r
+\r
if(codec_context_ &&\r
(codec_context_->sample_rate != static_cast<int>(format_desc_.audio_sample_rate) || \r
codec_context_->channels != static_cast<int>(format_desc_.audio_channels)) ||\r
void audio_decoder::push(const std::shared_ptr<AVPacket>& packet){impl_->push(packet);}\r
bool audio_decoder::ready() const{return impl_->ready();}\r
std::vector<std::shared_ptr<std::vector<int16_t>>> audio_decoder::poll(){return impl_->poll();}\r
+int64_t audio_decoder::nb_frames() const{return impl_->nb_frames_;}\r
}
\ No newline at end of file
bool ready() const;\r
std::vector<std::shared_ptr<std::vector<int16_t>>> poll();\r
\r
+ int64_t nb_frames() const;\r
+\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
\r
tbb::task_group tasks_;\r
\r
+ int start_;\r
+\r
public:\r
explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, const std::wstring& filter, bool loop, int start, int length) \r
: filename_(filename)\r
, video_decoder_(input_.context(), frame_factory, filter)\r
, audio_decoder_(input_.context(), frame_factory->get_video_format_desc())\r
, muxer_(video_decoder_.fps(), format_desc_, frame_factory)\r
+ , start_(start)\r
{\r
graph_->add_guide("frame-time", 0.5);\r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
BOOST_FOREACH(auto& video, video_frames)\r
muxer_.push(video); \r
}\r
+\r
+ virtual int64_t nb_frames() const\r
+ {\r
+ return std::max<int64_t>(0, video_decoder_.nb_frames()-start_);//std::max(video_decoder_.nb_frames(), audio_decoder_.nb_frames());\r
+ }\r
\r
virtual std::wstring print() const\r
{\r
std::unique_ptr<filter> filter_;\r
\r
double fps_;\r
+ int64_t nb_frames_;\r
public:\r
explicit implementation(const std::shared_ptr<AVFormatContext>& context, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filter) \r
: frame_factory_(frame_factory)\r
, mode_(core::video_mode::invalid)\r
, filter_(filter.empty() ? nullptr : new caspar::filter(filter))\r
, fps_(frame_factory_->get_video_format_desc().fps)\r
+ , nb_frames_(0)\r
{\r
AVCodec* dec;\r
index_ = av_find_best_stream(context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);\r
return;\r
\r
codec_context_.reset(context->streams[index_]->codec, tbb_avcodec_close);\r
-\r
+ \r
// Some files give an invalid time_base numerator, try to fix it.\r
if(codec_context_ && codec_context_->time_base.num == 1)\r
codec_context_->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(codec_context_->time_base.den)))-1)); \r
+ \r
+ nb_frames_ = context->streams[index_]->nb_frames;\r
+ if(nb_frames_ == 0)\r
+ nb_frames_ = context->streams[index_]->duration;// * context->streams[index_]->time_base.den;\r
\r
fps_ = static_cast<double>(codec_context_->time_base.den) / static_cast<double>(codec_context_->time_base.num);\r
if(double_rate(filter))\r
bool video_decoder::ready() const{return impl_->ready();}\r
core::video_mode::type video_decoder::mode(){return impl_->mode();}\r
double video_decoder::fps() const{return impl_->fps();}\r
+int64_t video_decoder::nb_frames() const{return impl_->nb_frames_;}\r
}
\ No newline at end of file
\r
core::video_mode::type mode();\r
\r
+ int64_t nb_frames() const;\r
+\r
double fps() const;\r
private:\r
struct implementation;\r
{ \r
const std::wstring filename_;\r
std::vector<safe_ptr<core::basic_frame>> frames_;\r
- size_t pixel_offset_;\r
+ size_t delta_;\r
core::video_format_desc format_desc_;\r
size_t width_;\r
size_t height_;\r
\r
explicit image_scroll_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, size_t speed) \r
: filename_(filename)\r
- , pixel_offset_(0)\r
+ , delta_(0)\r
, format_desc_(frame_factory->get_video_format_desc())\r
, speed_(speed)\r
{\r
frames_.push_back(frame);\r
}\r
}\r
- //else\r
- //{\r
- // while(count > 0)\r
- // {\r
- // auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), format_desc_.width, height_);\r
- // if(count >= frame->image_data().size())\r
- // { \r
- // count -= frame->image_data().size();\r
- // }\r
- // else\r
- // {\r
- // fast_memclr(frame->image_data().begin(), frame->image_data().size()); \r
- // //std::copy_n(bytes, count, frame->image_data().begin() + format_desc_.size - count);\r
- // count = 0;\r
- // }\r
- // \r
- // frame->commit();\r
- // frames_.push_back(frame);\r
- // }\r
- //}\r
+ else\r
+ {\r
+ int i = 0;\r
+ while(count > 0)\r
+ {\r
+ auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), format_desc_.width, height_);\r
+ if(count >= frame->image_data().size())\r
+ { \r
+ for(size_t y = 0; y < height_; ++y)\r
+ std::copy_n(bytes + i * format_desc_.width*4 + y * width_*4, format_desc_.width*4, frame->image_data().begin() + y * format_desc_.width*4);\r
+ \r
+ ++i;\r
+ count -= frame->image_data().size();\r
+ }\r
+ else\r
+ {\r
+ //fast_memclr(frame->image_data().begin(), frame->image_data().size()); \r
+ //int width2 = width_ % format_desc_.width;\r
+ //for(size_t y = 0; y < height_; ++y)\r
+ // std::copy_n(bytes + i * format_desc_.size*4 + y * width2*4, format_desc_.width*4, frame->image_data().begin() + y * format_desc_.width*4);\r
+\r
+ count = 0;\r
+ }\r
+ \r
+ frame->commit();\r
+ frames_.push_back(frame);\r
+ }\r
+ }\r
+\r
+ std::reverse(frames_.begin(), frames_.end());\r
}\r
\r
// frame_producer\r
\r
virtual safe_ptr<core::basic_frame> receive()\r
{\r
- pixel_offset_ += speed_;\r
+ delta_ += speed_;\r
\r
if(frames_.empty())\r
return core::basic_frame::eof();\r
-\r
- for(size_t n = 0; n < frames_.size(); ++n)\r
- frames_[n]->get_image_transform().set_fill_translation(0.0, -0.5*(n+1) + pixel_offset_ * 0.5/static_cast<double>(format_desc_.height));\r
+ \r
+ if(height_ > format_desc_.height)\r
+ {\r
+ for(size_t n = 0; n < frames_.size(); ++n)\r
+ frames_[n]->get_image_transform().set_fill_translation(0.0, -0.5*(n+1) + delta_ * 0.5/static_cast<double>(format_desc_.height));\r
+ }\r
+ else\r
+ {\r
+ for(size_t n = 0; n < frames_.size(); ++n)\r
+ frames_[n]->get_image_transform().set_fill_translation(-0.5*(n+1) + delta_ * 0.5/static_cast<double>(format_desc_.height), 0.0);\r
+ }\r
\r
return core::basic_frame(frames_);\r
}\r
if(pFP == frame_producer::empty())\r
BOOST_THROW_EXCEPTION(file_not_found() << msg_info(_parameters.size() > 0 ? narrow(_parameters[0]) : ""));\r
\r
+ bool auto_play = std::find(_parameters.begin(), _parameters.end(), L"AUTO") != _parameters.end();\r
+\r
auto pFP2 = create_transition_producer(GetChannel()->get_video_format_desc().mode, pFP, transitionInfo);\r
- GetChannel()->stage()->load(GetLayerIndex(), pFP2); // TODO: LOOP\r
+ GetChannel()->stage()->load(GetLayerIndex(), pFP2, false, auto_play ? transitionInfo.duration : -1); // TODO: LOOP\r
\r
CASPAR_LOG(info) << "Loaded " << _parameters[0] << TEXT(" successfully to background");\r
SetReplyString(TEXT("202 LOADBG OK\r\n"));\r
</consumers>\r
<channels>\r
<channel>\r
- <video-mode>720p5000</video-mode>\r
+ <video-mode>PAL</video-mode>\r
<consumers>\r
- <screen></screen>\r
+ <screen>\r
+ <device>1</device>\r
+ </screen>\r
<audio></audio>\r
</consumers>\r
</channel>\r