virtual safe_ptr<frame_producer> get_following_producer() const override {return (*producer_)->get_following_producer();}\r
virtual void set_leading_producer(const safe_ptr<frame_producer>& producer) override {(*producer_)->set_leading_producer(producer);}\r
virtual int64_t nb_frames() const override {return (*producer_)->nb_frames();}\r
+ virtual int64_t file_nb_frames() const override {return (*producer_)->file_nb_frames();}\r
+ virtual int64_t frame_number() const override {return (*producer_)->frame_number();}\r
+ virtual int64_t file_frame_number() const override {return (*producer_)->file_frame_number();}\r
};\r
\r
safe_ptr<core::frame_producer> create_destroy_proxy(safe_ptr<core::frame_producer>&& producer)\r
virtual void set_leading_producer(const safe_ptr<frame_producer>&) {} // nothrow\r
\r
virtual int64_t nb_frames() const {return std::numeric_limits<int>::max();}\r
+ virtual int64_t file_nb_frames() const {return nb_frames();}\r
+\r
+ virtual int64_t frame_number() const {return 0;}\r
+ virtual int64_t file_frame_number() const {return frame_number();}\r
\r
virtual safe_ptr<basic_frame> receive(int hints) = 0;\r
virtual safe_ptr<core::basic_frame> last_frame() const = 0;\r
layer_status status() const\r
{\r
layer_status status;\r
- status.foreground = foreground_->print();\r
- status.background = background_->print();\r
- status.is_paused = is_paused_;\r
- status.total_frames = foreground_->nb_frames();\r
- status.current_frame = frame_number_;\r
+ status.foreground = foreground_->print();\r
+ status.background = background_->print();\r
+ status.is_paused = is_paused_;\r
+ status.nb_frames = foreground_->nb_frames();\r
+ status.frame_number = std::max(frame_number_, foreground_->frame_number());\r
+ status.file_nb_frames = foreground_->file_nb_frames();\r
+ status.file_frame_number = foreground_->file_frame_number();\r
\r
return status;\r
}\r
std::wstring foreground;\r
std::wstring background;\r
bool is_paused;\r
- int64_t total_frames;\r
- int64_t current_frame;\r
+ int64_t nb_frames;\r
+ int64_t frame_number;\r
+ int64_t file_nb_frames;\r
+ int64_t file_frame_number;\r
};\r
\r
class layer : boost::noncopyable\r
{\r
return std::min(fill_producer_->nb_frames(), key_producer_->nb_frames());\r
}\r
+ \r
+ virtual int64_t file_nb_frames() const override\r
+ {\r
+ return std::min(fill_producer_->file_nb_frames(), key_producer_->file_nb_frames());\r
+ }\r
+ \r
+ virtual int64_t frame_number() const override\r
+ {\r
+ return std::max(fill_producer_->frame_number(), key_producer_->frame_number());\r
+ }\r
+ \r
+ virtual int64_t file_frame_number() const override\r
+ {\r
+ return std::max(fill_producer_->file_frame_number(), key_producer_->file_frame_number());\r
+ }\r
};\r
\r
safe_ptr<frame_producer> create_separated_producer(const safe_ptr<frame_producer>& fill, const safe_ptr<frame_producer>& key)\r
{\r
return get_following_producer()->nb_frames();\r
}\r
+ \r
+ virtual int64_t file_nb_frames() const override\r
+ {\r
+ return get_following_producer()->file_nb_frames();\r
+ }\r
+ \r
+ virtual int64_t frame_number() const override\r
+ {\r
+ return get_following_producer()->frame_number();\r
+ }\r
+ \r
+ virtual int64_t file_frame_number() const override\r
+ {\r
+ return get_following_producer()->file_frame_number();\r
+ }\r
\r
virtual std::wstring print() const override\r
{\r
std::queue<safe_ptr<AVPacket>> packets_;\r
\r
const int64_t nb_frames_;\r
+ tbb::atomic<size_t> file_frame_number_;\r
public:\r
explicit implementation(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc) \r
: format_desc_(format_desc) \r
, buffer1_(AVCODEC_MAX_AUDIO_FRAME_SIZE*2)\r
, nb_frames_(context->streams[index_]->nb_frames)\r
{ \r
+ file_frame_number_ = 0;\r
CASPAR_LOG(debug) << "[audio_decoder] " << context->streams[index_]->codec->codec->long_name; \r
}\r
\r
if(!packet)\r
return;\r
\r
- if(packet->stream_index == index_ || packet == flush_packet())\r
+ if(packet->stream_index == index_ || packet->data == nullptr)\r
packets_.push(make_safe_ptr(packet));\r
} \r
\r
\r
auto packet = packets_.front();\r
\r
- if(packet == flush_packet())\r
+ if(packet->data == nullptr)\r
{\r
packets_.pop();\r
+ file_frame_number_ = static_cast<size_t>(packet->pos);\r
avcodec_flush_buffers(codec_context_.get());\r
return flush_audio();\r
}\r
const auto n_samples = buffer1_.size() / av_get_bytes_per_sample(AV_SAMPLE_FMT_S32);\r
const auto samples = reinterpret_cast<int32_t*>(buffer1_.data());\r
\r
+ ++file_frame_number_;\r
+\r
return std::make_shared<core::audio_buffer>(samples, samples + n_samples);\r
}\r
\r
bool audio_decoder::ready() const{return impl_->ready();}\r
std::shared_ptr<core::audio_buffer> audio_decoder::poll(){return impl_->poll();}\r
int64_t audio_decoder::nb_frames() const{return impl_->nb_frames_;}\r
+size_t audio_decoder::file_frame_number() const{return impl_->file_frame_number_;}\r
\r
}}
\ No newline at end of file
std::shared_ptr<core::audio_buffer> poll();\r
\r
int64_t nb_frames() const;\r
-\r
+ \r
+ size_t file_frame_number() const;\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
safe_ptr<core::basic_frame> last_frame_;\r
\r
std::queue<safe_ptr<core::basic_frame>> frame_buffer_;\r
+\r
+ int64_t frame_number_;\r
+ int64_t file_frame_number_;;\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, size_t length) \r
, loop_(loop)\r
, length_(length)\r
, last_frame_(core::basic_frame::empty())\r
+ , frame_number_(0)\r
{\r
graph_->add_guide("frame-time", 0.5);\r
graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));\r
\r
graph_->set_text(print());\r
\r
+ ++frame_number_;\r
return last_frame_;\r
}\r
\r
return nb_frames - start_;\r
}\r
\r
+ virtual int64_t file_nb_frames() const override\r
+ {\r
+ // This function estimates nb_frames until input has read all packets for one loop, at which point the count should be accurate.\r
+\r
+ int64_t nb_frames = input_.nb_frames();\r
+ if(input_.nb_loops() < 1) // input still hasn't counted all frames\r
+ {\r
+ auto video_nb_frames = video_decoder_ ? video_decoder_->nb_frames() : std::numeric_limits<int64_t>::max();\r
+ //auto audio_nb_frames = audio_decoder_ ? audio_decoder_->nb_frames() : std::numeric_limits<int64_t>::max();\r
+\r
+ nb_frames = std::max(nb_frames, video_nb_frames);\r
+ }\r
+ return nb_frames;\r
+ }\r
+\r
+ virtual int64_t frame_number() const override\r
+ {\r
+ return frame_number_;\r
+ }\r
+\r
+ virtual int64_t file_frame_number() const override\r
+ {\r
+ return file_frame_number_;\r
+ }\r
+\r
virtual boost::unique_future<std::wstring> call(const std::wstring& param) override\r
{\r
boost::promise<std::wstring> promise;\r
}\r
\r
// ffmpeg_producer\r
-\r
- int64_t file_nb_frames() const\r
- {\r
- // This function estimates nb_frames until input has read all packets for one loop, at which point the count should be accurate.\r
-\r
- int64_t nb_frames = input_.nb_frames();\r
- if(input_.nb_loops() < 1) // input still hasn't counted all frames\r
- {\r
- auto video_nb_frames = video_decoder_ ? video_decoder_->nb_frames() : std::numeric_limits<int64_t>::max();\r
- //auto audio_nb_frames = audio_decoder_ ? audio_decoder_->nb_frames() : std::numeric_limits<int64_t>::max();\r
-\r
- nb_frames = std::max(nb_frames, video_nb_frames);\r
- }\r
- return nb_frames;\r
- }\r
- \r
+ \r
std::wstring do_call(const std::wstring& param)\r
{\r
static const boost::wregex loop_exp(L"LOOP\\s*(?<VALUE>\\d?)", boost::regex::icase);\r
tbb::parallel_invoke(\r
[&]\r
{\r
- if(!muxer_->video_ready() && video_decoder_) \r
- video = video_decoder_->poll(); \r
+ if(!muxer_->video_ready() && video_decoder_) \r
+ {\r
+ video = video_decoder_->poll(); \r
+ file_frame_number_ = video_decoder_->file_frame_number();\r
+ }\r
},\r
[&]\r
{ \r
- if(!muxer_->audio_ready() && audio_decoder_) \r
- audio = audio_decoder_->poll(); \r
+ if(!muxer_->audio_ready() && audio_decoder_) \r
+ {\r
+ audio = audio_decoder_->poll(); \r
+ file_frame_number_ = video_decoder_->file_frame_number();\r
+ } \r
});\r
-\r
+ \r
muxer_->push(video, hints);\r
muxer_->push(audio);\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
is_eof_ = false;\r
buffer_cond_.notify_all();\r
\r
- buffer_.push(flush_packet());\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
void seek(int64_t target)\r
\r
namespace caspar { namespace ffmpeg {\r
\r
-safe_ptr<AVPacket> flush_packet()\r
-{\r
- static auto packet = create_packet();\r
- return packet;\r
-}\r
-\r
std::shared_ptr<core::audio_buffer> flush_audio()\r
{\r
static std::shared_ptr<core::audio_buffer> audio(new core::audio_buffer());\r
}\r
\r
namespace ffmpeg {\r
- \r
- \r
-safe_ptr<AVPacket> flush_packet();\r
+ \r
std::shared_ptr<core::audio_buffer> flush_audio();\r
std::shared_ptr<core::audio_buffer> empty_audio();\r
std::shared_ptr<AVFrame> flush_video();\r
const size_t height_;\r
bool is_progressive_;\r
\r
+ tbb::atomic<size_t> file_frame_number_;\r
+\r
public:\r
explicit implementation(const safe_ptr<AVFormatContext>& context) \r
: codec_context_(open_codec(*context, AVMEDIA_TYPE_VIDEO, index_))\r
, width_(codec_context_->width)\r
, height_(codec_context_->height)\r
{\r
+ file_frame_number_ = 0;\r
CASPAR_LOG(debug) << "[video_decoder] " << context->streams[index_]->codec->codec->long_name;\r
}\r
\r
if(!packet)\r
return;\r
\r
- if(packet->stream_index == index_ || packet == flush_packet())\r
+ if(packet->stream_index == index_ || packet->data == nullptr)\r
packets_.push(make_safe_ptr(packet));\r
}\r
\r
\r
auto packet = packets_.front();\r
\r
- if(packet == flush_packet())\r
+ if(packet->data == nullptr)\r
{ \r
if(codec_context_->codec->capabilities & CODEC_CAP_DELAY)\r
{\r
- AVPacket pkt;\r
- av_init_packet(&pkt);\r
- pkt.data = nullptr;\r
- pkt.size = 0;\r
-\r
- auto video = decode(pkt);\r
+ auto video = decode(*packet);\r
if(video)\r
return video;\r
}\r
- \r
+ \r
packets_.pop();\r
+ file_frame_number_ = static_cast<size_t>(packet->pos);\r
avcodec_flush_buffers(codec_context_.get());\r
return flush_video(); \r
}\r
if(decoded_frame->repeat_pict > 0)\r
CASPAR_LOG(warning) << "[video_decoder] Field repeat_pict not implemented.";\r
\r
+ ++file_frame_number_;\r
+\r
return decoded_frame;\r
}\r
\r
size_t video_decoder::width() const{return impl_->width_;}\r
size_t video_decoder::height() const{return impl_->height_;}\r
bool video_decoder::is_progressive() const{return impl_->is_progressive_;}\r
+size_t video_decoder::file_frame_number() const{return impl_->file_frame_number_;}\r
\r
}}
\ No newline at end of file
int64_t nb_frames() const;\r
double fps() const;\r
bool is_progressive() const;\r
+\r
+ size_t file_frame_number() const;\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
status_text\r
<< L"201 STATUS OK\r\n"\r
<< L"<layer>"\r
- << L"\n\t<index>" << GetLayerIndex() << L"</index>"\r
- << L"\n\t<foreground>" << status.foreground << L"</foreground>"\r
- << L"\n\t<background>" << status.background << L"</background>"\r
- << L"\n\t<status>" << (status.is_paused ? L"paused" : L"playing") << L"</status>"\r
- << L"\n\t<total-frames>" << (status.total_frames == std::numeric_limits<int64_t>::max() ? 0 : status.total_frames) << L"</total-frames>"\r
- << L"\n\t<current-frame>" << status.current_frame << L"</current-frame>"\r
+ << L"\n\t<index>" << GetLayerIndex() << L"</index>"\r
+ << L"\n\t<foreground>" << status.foreground << L"</foreground>"\r
+ << L"\n\t<background>" << status.background << L"</background>"\r
+ << L"\n\t<status>" << (status.is_paused ? L"paused" : L"playing") << L"</status>"\r
+ << L"\n\t<nb-frames>" << (status.nb_frames == std::numeric_limits<int64_t>::max() ? 0 : status.nb_frames) << L"</nb-frames>"\r
+ << L"\n\t<frame-number>" << status.frame_number << L"</frame-number>"\r
+ << L"\n\t<file-nb-frames>" << (status.file_nb_frames == std::numeric_limits<int64_t>::max() ? 0 : status.file_nb_frames) << L"</file-nb-frames>"\r
+ << L"\n\t<file-frame-number>" << status.file_frame_number << L"</file-frame-number>"\r
<< L"\n</layer>"\r
<< L"\r\n";\r
\r
</paths>\r
<channels>\r
<channel>\r
- <video-mode>PAL</video-mode>\r
+ <video-mode>NTSC</video-mode>\r
<consumers>\r
<screen>\r
</screen>\r