\r
struct audio_decoder::implementation : boost::noncopyable\r
{ \r
- AVCodecContext& codec_context_;\r
- \r
- const core::video_format_desc format_desc_;\r
-\r
- std::vector<short> current_chunk_;\r
-\r
- std::vector<std::vector<short>> chunks_;\r
-\r
+ AVCodecContext& codec_context_; \r
+ const core::video_format_desc format_desc_;\r
+ std::vector<short> current_chunk_;\r
+ std::vector<std::vector<short>> chunks_; \r
+ size_t frame_number_;\r
public:\r
explicit implementation(AVCodecContext& codec_context, const core::video_format_desc& format_desc) \r
: codec_context_(codec_context)\r
- , format_desc_(format_desc) \r
+ , format_desc_(format_desc) \r
+ , frame_number_(0)\r
{\r
if(codec_context_.sample_rate != static_cast<int>(format_desc_.audio_sample_rate) || \r
codec_context_.channels != static_cast<int>(format_desc_.audio_channels))\r
arg_name_info("codec_context"));\r
}\r
}\r
- \r
- void push(std::shared_ptr<AVPacket>&& audio_packet)\r
- { \r
+ \r
+ void push(const std::shared_ptr<AVPacket>& audio_packet)\r
+ { \r
if(!audio_packet)\r
- return;\r
- \r
- if(audio_packet->size == 0)\r
- {\r
+ { \r
avcodec_flush_buffers(&codec_context_);\r
+ current_chunk_.clear();\r
+ frame_number_ = 0;\r
return;\r
}\r
-\r
+ \r
auto s = current_chunk_.size();\r
- current_chunk_.resize(s + 4*format_desc_.audio_sample_rate*2+FF_INPUT_BUFFER_PADDING_SIZE/2);\r
+ current_chunk_.resize(s + 4*format_desc_.audio_sample_rate*2+FF_INPUT_BUFFER_PADDING_SIZE/2, 0);\r
\r
int written_bytes = (current_chunk_.size() - s)*2 - FF_INPUT_BUFFER_PADDING_SIZE;\r
const int errn = avcodec_decode_audio3(&codec_context_, ¤t_chunk_[s], &written_bytes, audio_packet.get());\r
\r
void pop()\r
{\r
+ ++frame_number_;\r
chunks_.pop_back();\r
}\r
};\r
\r
audio_decoder::audio_decoder(AVCodecContext& codec_context, const core::video_format_desc& format_desc) : impl_(new implementation(codec_context, format_desc)){}\r
-void audio_decoder::push(std::shared_ptr<AVPacket>&& audio_packet){impl_->push(std::move(audio_packet));}\r
+void audio_decoder::push(const std::shared_ptr<AVPacket>& audio_packet){impl_->push(std::move(audio_packet));}\r
bool audio_decoder::empty() const {return impl_->empty();}\r
std::vector<short> audio_decoder::front() {return impl_->front();}\r
void audio_decoder::pop(){impl_->pop();}\r
+size_t audio_decoder::frame_number() const{return impl_->frame_number_;}\r
}
\ No newline at end of file
{\r
public:\r
explicit audio_decoder(AVCodecContext& codec_context, const core::video_format_desc& format_desc);\r
- void push(std::shared_ptr<AVPacket>&& audio_packet);\r
- \r
- bool empty() const;\r
+\r
+ void push(const std::shared_ptr<AVPacket>& audio_packet);\r
std::vector<short> front();\r
- void pop();\r
+ void pop(); \r
+ bool empty() const;\r
+\r
+ size_t frame_number() const;\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
#include "audio/audio_decoder.h"\r
#include "video/video_decoder.h"\r
\r
+#include <common/utility/assert.h>\r
#include <common/utility/timer.h>\r
#include <common/diagnostics/graph.h>\r
\r
: filename_(filename)\r
, graph_(diagnostics::create_graph(narrow(print())))\r
, frame_factory_(frame_factory) \r
- , input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop, start)\r
+ , input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop, start, length)\r
{\r
graph_->add_guide("frame-time", 0.5);\r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
(\r
[&]\r
{\r
+ if(!video_decoder_)\r
+ return;\r
+\r
std::shared_ptr<AVPacket> pkt;\r
- for(int n = 0; n < 8 && video_decoder_ && video_decoder_->empty() && input_.try_pop_video_packet(pkt); ++n)\r
- video_decoder_->push(std::move(pkt));\r
+ for(int n = 0; n < 16 && video_decoder_->empty() && input_.try_pop_video_packet(pkt); ++n) \r
+ video_decoder_->push(pkt); \r
}, \r
[&]\r
{\r
- std::shared_ptr<AVPacket> pkt;\r
- for(int n = 0; n < 8 && audio_decoder_ && audio_decoder_->empty() && input_.try_pop_audio_packet(pkt); ++n)\r
- audio_decoder_->push(std::move(pkt));\r
+ if(!audio_decoder_)\r
+ return;\r
+ \r
+ std::shared_ptr<AVPacket> pkt; \r
+ for(int n = 0; n < 16 && audio_decoder_->empty() && input_.try_pop_audio_packet(pkt); ++n) \r
+ audio_decoder_->push(pkt); \r
+ }\r
+ );\r
+ \r
+ // Sync up audio with video on start. Last frame sometimes have more samples than required. Remove those.\r
+ if(audio_decoder_ && video_decoder_ && video_decoder_->frame_number() == 0)\r
+ {\r
+ std::shared_ptr<AVPacket> pkt;\r
+ while(audio_decoder_->frame_number() > 0 && input_.try_pop_audio_packet(pkt))\r
+ {\r
+ audio_decoder_->push(pkt);\r
+ if(audio_decoder_->frame_number() > 0)\r
+ audio_decoder_->pop();\r
}\r
- ); \r
+ \r
+ for(int n = 0; n < 16 && audio_decoder_->empty() && input_.try_pop_audio_packet(pkt); ++n) \r
+ audio_decoder_->push(pkt); \r
+ }\r
}\r
\r
safe_ptr<core::basic_frame> decode_frame()\r
\r
frame->audio_data() = std::move(audio_decoder_->front());\r
audio_decoder_->pop();\r
-\r
+ \r
return frame;\r
}\r
else if(video_decoder_ && !video_decoder_->empty() && !audio_decoder_)\r
auto frame = std::move(video_decoder_->front()); \r
video_decoder_->pop();\r
frame->get_audio_transform().set_has_audio(false); \r
-\r
+ \r
return frame;\r
}\r
else if(audio_decoder_ && !audio_decoder_->empty() && !video_decoder_)\r
\r
public:\r
\r
- stream()\r
- : index_(-1)\r
+ stream() : index_(-1)\r
{\r
buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
}\r
\r
+ ~stream()\r
+ {\r
+ CASPAR_LOG(trace) << "##: " << size();\r
+ }\r
+\r
int open(std::shared_ptr<AVFormatContext>& fctx, AVMediaType media_type)\r
{ \r
const auto streams = boost::iterator_range<AVStream**>(fctx->streams, fctx->streams+fctx->nb_streams);\r
\r
void push(const std::shared_ptr<AVPacket>& pkt)\r
{\r
- if(pkt->stream_index != index_)\r
+ if(pkt && pkt->stream_index != index_)\r
return;\r
\r
- av_dup_packet(pkt.get());\r
- buffer_.push(pkt); \r
- }\r
-\r
- void flush()\r
- {\r
- if(index_ == -1)\r
- return;\r
+ if(pkt)\r
+ av_dup_packet(pkt.get());\r
\r
- std::shared_ptr<AVPacket> flsh_pkt(new AVPacket);\r
- flsh_pkt->size = 0;\r
- buffer_.push(flsh_pkt); \r
+ buffer_.push(pkt); \r
}\r
\r
+ int index() const {return index_;}\r
+ \r
const std::shared_ptr<AVCodecContext>& ctx() { return ctx_; }\r
\r
operator bool(){return ctx_ != nullptr;}\r
, executor_(print())\r
, start_(std::max(start, 0))\r
{ \r
- graph_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f));\r
+ graph_->set_color("audio-input-buffer", diagnostics::color(0.5f, 1.0f, 0.2f));\r
+ graph_->set_color("video-input-buffer", diagnostics::color(0.2f, 0.5f, 1.0f));\r
graph_->set_color("seek", diagnostics::color(0.5f, 1.0f, 0.5f)); \r
\r
int errn;\r
stop();\r
}\r
\r
-\r
bool try_pop_video_packet(std::shared_ptr<AVPacket>& packet)\r
{\r
return video_stream_.try_pop(packet);\r
}\r
\r
bool try_pop_audio_packet(std::shared_ptr<AVPacket>& packet)\r
- {\r
+ { \r
return audio_stream_.try_pop(packet);\r
}\r
\r
audio_stream_.push(read_packet);\r
}\r
\r
- graph_->update_value("input-buffer", static_cast<float>(std::max(video_stream_.size(), audio_stream_.size()))/static_cast<float>(PACKET_BUFFER_COUNT)); \r
+ graph_->update_value("video-input-buffer", static_cast<float>(video_stream_.size())/static_cast<float>(PACKET_BUFFER_COUNT)); \r
+ graph_->update_value("audio-input-buffer", static_cast<float>(audio_stream_.size())/static_cast<float>(PACKET_BUFFER_COUNT)); \r
}\r
catch(...)\r
{\r
\r
void seek_frame(int64_t frame, int flags = 0)\r
{ \r
+ static const AVRational base_q = {1, AV_TIME_BASE};\r
+\r
// Convert from frames into seconds.\r
- const auto ts = frame*static_cast<int64_t>(AV_TIME_BASE/fps_);\r
+ auto seek_target = frame*static_cast<int64_t>(AV_TIME_BASE/fps_);\r
+\r
+ int stream_index = video_stream_.index() >= 0 ? video_stream_.index() : audio_stream_.index();\r
+\r
+ if(stream_index >= 0) \r
+ seek_target = av_rescale_q(seek_target, base_q, format_context_->streams[stream_index]->time_base);\r
\r
- const int errn = av_seek_frame(format_context_.get(), -1, ts, flags | AVSEEK_FLAG_FRAME);\r
+ const int errn = av_seek_frame(format_context_.get(), stream_index, seek_target, flags);\r
if(errn < 0)\r
{ \r
BOOST_THROW_EXCEPTION(\r
boost::errinfo_errno(AVUNERROR(errn)));\r
}\r
\r
- video_stream_.flush();\r
- audio_stream_.flush();\r
+ video_stream_.push(nullptr);\r
+ audio_stream_.push(nullptr);\r
} \r
\r
bool is_eof(int errn)\r
}\r
};\r
\r
-input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start) \r
+input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start, int length) \r
: impl_(new implementation(graph, filename, loop, start)){}\r
const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_stream_.ctx();}\r
const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_stream_.ctx();}\r
#include <memory>\r
#include <string>\r
\r
+#include <boost/iterator/iterator_facade.hpp>\r
+\r
struct AVCodecContext;\r
\r
namespace caspar {\r
class input : boost::noncopyable\r
{\r
public:\r
- explicit input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start);\r
+ explicit input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start, int length);\r
const std::shared_ptr<AVCodecContext>& get_video_codec_context() const;\r
const std::shared_ptr<AVCodecContext>& get_audio_codec_context() const;\r
\r
bool try_pop_video_packet(std::shared_ptr<AVPacket>& packet);\r
bool try_pop_audio_packet(std::shared_ptr<AVPacket>& packet);\r
\r
- bool has_video_packet() const;\r
- bool has_audio_packet() const;\r
bool is_running() const;\r
double fps() const;\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
};\r
+//\r
+//class input_video_iterator : public boost::iterator_facade<input_video_iterator, std::shared_ptr<AVPacket>, boost::forward_traversal_tag>\r
+//{\r
+// std::shared_ptr<AVPacket> pkt_;\r
+// input* input_;\r
+//public:\r
+// input_video_iterator() : input_(nullptr){}\r
+//\r
+// input_video_iterator(input& input)\r
+// : input_(&input) {}\r
+//\r
+// input_video_iterator(const input_video_iterator& other)\r
+// : input_(other.input_) {}\r
+//\r
+// private:\r
+// friend class boost::iterator_core_access;\r
+//\r
+// void increment() \r
+// {\r
+// if(input_ && !input_->try_pop_video_packet(pkt_))\r
+// input_ = nullptr;\r
+// }\r
+//\r
+// bool equal(input_video_iterator const& other) const\r
+// {\r
+// return input_ == other.input_;\r
+// }\r
+//\r
+// std::shared_ptr<AVPacket> const& dereference() const { return pkt_; }\r
+//};\r
\r
\r
}\r
const PixelFormat pix_fmt_;\r
core::pixel_format_desc desc_;\r
std::vector<safe_ptr<core::write_frame>> frames_;\r
+ size_t frame_number_;\r
\r
public:\r
explicit implementation(AVCodecContext& codec_context, const safe_ptr<core::frame_factory>& frame_factory) \r
, height_(codec_context_.height)\r
, pix_fmt_(codec_context_.pix_fmt)\r
, desc_(get_pixel_format_desc(pix_fmt_, width_, height_))\r
+ , frame_number_(0)\r
{\r
if(desc_.pix_fmt == core::pixel_format::invalid)\r
{\r
boost::errinfo_api_function("sws_getContext"));\r
}\r
}\r
- \r
- void push(std::shared_ptr<AVPacket>&& video_packet)\r
+\r
+ void push(const std::shared_ptr<AVPacket>& video_packet)\r
{ \r
if(!video_packet)\r
- return;\r
-\r
- if(video_packet->size == 0)\r
- {\r
+ { \r
avcodec_flush_buffers(&codec_context_);\r
+ frame_number_ = 0;\r
return;\r
}\r
- \r
+\r
safe_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
\r
int frame_finished = 0;\r
\r
void pop()\r
{\r
+ ++frame_number_;\r
frames_.pop_back();\r
}\r
};\r
\r
video_decoder::video_decoder(AVCodecContext& codec_context, const safe_ptr<core::frame_factory>& frame_factory) : impl_(new implementation(codec_context, frame_factory)){}\r
-void video_decoder::push(std::shared_ptr<AVPacket>&& video_packet){impl_->push(std::move(video_packet));}\r
+void video_decoder::push(const std::shared_ptr<AVPacket>& video_packet){impl_->push(std::move(video_packet));}\r
bool video_decoder::empty() const {return impl_->empty();}\r
safe_ptr<core::write_frame> video_decoder::front() {return impl_->front();}\r
void video_decoder::pop(){impl_->pop();}\r
+size_t video_decoder::frame_number() const{return impl_->frame_number_;}\r
\r
}
\ No newline at end of file
{\r
public:\r
explicit video_decoder(AVCodecContext& codec_context, const safe_ptr<core::frame_factory>& frame_factory);\r
- void push(std::shared_ptr<AVPacket>&& video_packet); \r
+ void push(const std::shared_ptr<AVPacket>& video_packet); \r
\r
bool empty() const;\r
safe_ptr<core::write_frame> front();\r
void pop();\r
+ \r
+ size_t frame_number() const;\r
+\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
\r
int thread_execute2(AVCodecContext* s, int (*func)(AVCodecContext* c2, void* arg2, int, int), void* arg, int* ret, int count)\r
{ \r
- tbb::atomic<int> counter;\r
- counter = 0;\r
- \r
- // Execute s->thread_count number of tasks in parallel.\r
- tbb::parallel_for(0, s->thread_count, 1, [&](int threadnr) \r
- {\r
- while(true)\r
- {\r
- int jobnr = counter++;\r
- if(jobnr >= count)\r
- break;\r
-\r
- int r = func(s, arg, jobnr, threadnr);\r
- if (ret)\r
- ret[jobnr] = r;\r
- }\r
- });\r
-\r
- return 0;\r
+ tbb::atomic<int> counter; \r
+ counter = 0; \r
+\r
+ // Note: this will probably only work when tbb::task_scheduler_init::num_threads() < 16.\r
+ tbb::parallel_for(tbb::blocked_range<int>(0, count, 2), [&](const tbb::blocked_range<int> &r) \r
+ { \r
+ int threadnr = counter++; \r
+ for(int jobnr = r.begin(); jobnr != r.end(); ++jobnr)\r
+ { \r
+ int r = func(s, arg, jobnr, threadnr); \r
+ if (ret) \r
+ ret[jobnr] = r; \r
+ }\r
+ --counter;\r
+ }); \r
+\r
+ return 0; \r
}\r
\r
void thread_init(AVCodecContext* s)\r
\r
SMALL_RECT DisplayArea = {0, 0, 0, 0};\r
DisplayArea.Right = coord.X-1;\r
- DisplayArea.Bottom = coord.Y-1;\r
+ DisplayArea.Bottom = (coord.Y-1)/2;\r
SetConsoleWindowInfo(hOut, TRUE, &DisplayArea);\r
\r
// Set console title.\r