\r
struct audio_decoder::implementation : boost::noncopyable\r
{ \r
+ input& input_;\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
+ bool restarting_;\r
public:\r
- explicit implementation(AVCodecContext& codec_context, const core::video_format_desc& format_desc) \r
- : codec_context_(codec_context)\r
+ explicit implementation(input& input, const core::video_format_desc& format_desc) \r
+ : input_(input)\r
+ , codec_context_(*input_.get_audio_codec_context())\r
, format_desc_(format_desc) \r
, frame_number_(0)\r
+ , restarting_(false)\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(const std::shared_ptr<AVPacket>& audio_packet)\r
+ \r
+ std::deque<std::pair<int, std::vector<short>>> receive()\r
+ {\r
+ std::deque<std::pair<int, std::vector<short>>> result;\r
+ \r
+ std::shared_ptr<AVPacket> pkt;\r
+ for(int n = 0; n < 32 && result.empty() && input_.try_pop_audio_packet(pkt); ++n) \r
+ result = decode(pkt);\r
+\r
+ return result;\r
+ }\r
+\r
+ std::deque<std::pair<int, std::vector<short>>> decode(const std::shared_ptr<AVPacket>& audio_packet)\r
{ \r
+ std::deque<std::pair<int, std::vector<short>>> result;\r
+\r
if(!audio_packet)\r
{ \r
avcodec_flush_buffers(&codec_context_);\r
current_chunk_.clear();\r
frame_number_ = 0;\r
- return;\r
+ restarting_ = false;\r
+ return result;\r
}\r
+\r
+ if(restarting_)\r
+ return result;\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, 0);\r
const auto last = current_chunk_.end() - current_chunk_.size() % format_desc_.audio_samples_per_frame;\r
\r
for(auto it = current_chunk_.begin(); it != last; it += format_desc_.audio_samples_per_frame) \r
- chunks_.push_back(std::vector<short>(it, it + format_desc_.audio_samples_per_frame)); \r
+ result.push_back(std::make_pair(frame_number_++, std::vector<short>(it, it + format_desc_.audio_samples_per_frame))); \r
\r
current_chunk_.erase(current_chunk_.begin(), last);\r
- }\r
-\r
- bool empty() const\r
- {\r
- return chunks_.empty();\r
- }\r
\r
- std::vector<short> front()\r
- {\r
- return chunks_.front();\r
+ return result;\r
}\r
\r
- void pop()\r
+ void restart()\r
{\r
- ++frame_number_;\r
- chunks_.pop_back();\r
+ restarting_ = true;\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(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
+audio_decoder::audio_decoder(input& input, const core::video_format_desc& format_desc) : impl_(new implementation(input, format_desc)){}\r
+std::deque<std::pair<int, std::vector<short>>> audio_decoder::receive(){return impl_->receive();}\r
+void audio_decoder::restart(){impl_->restart();}\r
}
\ No newline at end of file
*/\r
#pragma once\r
\r
+#include "../input.h"\r
+\r
#include <core/video_format.h>\r
\r
#include <tbb/cache_aligned_allocator.h>\r
class audio_decoder : boost::noncopyable\r
{\r
public:\r
- explicit audio_decoder(AVCodecContext& codec_context, const core::video_format_desc& format_desc);\r
+ explicit audio_decoder(input& input, const core::video_format_desc& format_desc);\r
\r
- void push(const std::shared_ptr<AVPacket>& audio_packet);\r
- std::vector<short> front();\r
- void pop(); \r
- bool empty() const;\r
+ std::deque<std::pair<int, std::vector<short>>> receive();\r
\r
- size_t frame_number() const;\r
+ void restart();\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
input input_; \r
std::unique_ptr<video_decoder> video_decoder_;\r
std::unique_ptr<audio_decoder> audio_decoder_;\r
+\r
+ std::deque<std::pair<int, std::vector<short>>> audio_chunks_;\r
+ std::deque<std::pair<int, safe_ptr<core::write_frame>>> video_frames_;\r
public:\r
explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, bool loop, int start, int length) \r
: filename_(filename)\r
CASPAR_LOG(warning) << print() << L" Invalid framerate detected. This may cause distorted audio during playback. frame-time: " << frame_time;\r
\r
video_decoder_.reset(input_.get_video_codec_context() ? \r
- new video_decoder(*input_.get_video_codec_context(), frame_factory) : nullptr);\r
+ new video_decoder(input_, frame_factory) : nullptr);\r
\r
audio_decoder_.reset(input_.get_audio_codec_context() ? \r
- new audio_decoder(*input_.get_audio_codec_context(), frame_factory->get_video_format_desc()) : nullptr); \r
+ new audio_decoder(input_, frame_factory->get_video_format_desc()) : nullptr); \r
\r
// Fill buffers.\r
- decode_next_packets();\r
+ decode_packets();\r
}\r
\r
virtual safe_ptr<core::basic_frame> receive()\r
return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]";\r
}\r
\r
- void decode_next_packets()\r
+ void decode_packets()\r
{\r
tbb::parallel_invoke\r
(\r
[&]\r
{\r
- if(!video_decoder_)\r
- return;\r
-\r
- std::shared_ptr<AVPacket> pkt;\r
- for(int n = 0; n < 16 && video_decoder_->empty() && input_.try_pop_video_packet(pkt); ++n) \r
- video_decoder_->push(pkt); \r
+ if(video_decoder_ && video_frames_.empty())\r
+ video_frames_ = video_decoder_->receive(); \r
}, \r
[&]\r
{\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
+ if(audio_decoder_ && audio_chunks_.empty())\r
+ audio_chunks_ = audio_decoder_->receive(); \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
+ if(audio_decoder_ && video_decoder_ && !video_frames_.empty() && !audio_chunks_.empty() &&\r
+ video_frames_.front().first == 0 && audio_chunks_.front().first != 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
- for(int n = 0; n < 16 && audio_decoder_->empty() && input_.try_pop_audio_packet(pkt); ++n) \r
- audio_decoder_->push(pkt); \r
+ audio_decoder_->restart();\r
+ audio_chunks_ = audio_decoder_->receive(); \r
}\r
+ \r
+ CASPAR_ASSERT(!(video_decoder_ && audio_decoder_ && !video_frames_.empty() && !audio_chunks_.empty()) ||\r
+ video_frames_.front().first == audio_chunks_.front().first);\r
}\r
\r
safe_ptr<core::basic_frame> decode_frame()\r
{\r
- decode_next_packets();\r
+ decode_packets();\r
\r
- if(video_decoder_ && !video_decoder_->empty() && audio_decoder_ && !audio_decoder_->empty())\r
+ if(video_decoder_ && audio_decoder_ && !video_frames_.empty() && !audio_chunks_.empty())\r
{\r
- auto frame = std::move(video_decoder_->front()); \r
- video_decoder_->pop();\r
+ auto frame = std::move(video_frames_.front().second); \r
+ video_frames_.pop_front();\r
\r
- frame->audio_data() = std::move(audio_decoder_->front());\r
- audio_decoder_->pop();\r
+ frame->audio_data() = std::move(audio_chunks_.front().second);\r
+ audio_chunks_.pop_front();\r
\r
return frame;\r
}\r
- else if(video_decoder_ && !video_decoder_->empty() && !audio_decoder_)\r
+ else if(video_decoder_ && !audio_decoder_ && !video_frames_.empty())\r
{\r
- auto frame = std::move(video_decoder_->front()); \r
- video_decoder_->pop();\r
+ auto frame = std::move(video_frames_.front().second); \r
+ video_frames_.pop_front();\r
frame->get_audio_transform().set_has_audio(false); \r
\r
return frame;\r
}\r
- else if(audio_decoder_ && !audio_decoder_->empty() && !video_decoder_)\r
+ else if(audio_decoder_ && !video_decoder_ && !audio_chunks_.empty())\r
{\r
auto frame = frame_factory_->create_frame(this, 1, 1);\r
std::fill(frame->image_data().begin(), frame->image_data().end(), 0);\r
\r
- frame->audio_data() = std::move(audio_decoder_->front());\r
- audio_decoder_->pop();\r
+ frame->audio_data() = std::move(audio_chunks_.front().second);\r
+ audio_chunks_.pop_front();\r
\r
return frame;\r
}\r
{\r
buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
}\r
-\r
- ~stream()\r
- {\r
- CASPAR_LOG(trace) << "##: " << size();\r
- }\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
- const auto stream = boost::find_if(streams, [&](AVStream* stream) \r
+ const auto it = boost::find_if(streams, [&](AVStream* stream) \r
{\r
return stream && stream->codec->codec_type == media_type;\r
});\r
\r
- if(stream == streams.end()) \r
+ if(it == streams.end()) \r
return AVERROR_STREAM_NOT_FOUND;\r
\r
- auto codec = avcodec_find_decoder((*stream)->codec->codec_id); \r
+ auto codec = avcodec_find_decoder((*it)->codec->codec_id); \r
if(!codec)\r
return AVERROR_DECODER_NOT_FOUND;\r
\r
- index_ = (*stream)->index;\r
+ index_ = (*it)->index;\r
\r
- int errn = tbb_avcodec_open((*stream)->codec, codec);\r
+ int errn = tbb_avcodec_open((*it)->codec, codec);\r
if(errn >= 0)\r
{\r
- ctx_.reset((*stream)->codec, tbb_avcodec_close);\r
+ ctx_.reset((*it)->codec, tbb_avcodec_close);\r
\r
// Some files give an invalid time_base numerator, try to fix it.\r
if(ctx_ && ctx_->time_base.num == 1)\r
\r
bool try_pop_video_packet(std::shared_ptr<AVPacket>& packet)\r
{\r
- return video_stream_.try_pop(packet);\r
+ bool result = video_stream_.try_pop(packet);\r
+ if(result && !packet)\r
+ graph_->add_tag("video-input-buffer");\r
+ return result;\r
}\r
\r
bool try_pop_audio_packet(std::shared_ptr<AVPacket>& packet)\r
{ \r
- return audio_stream_.try_pop(packet);\r
+ bool result = audio_stream_.try_pop(packet);\r
+ if(result && !packet)\r
+ graph_->add_tag("audio-input-buffer");\r
+ return result;\r
}\r
\r
double fps()\r
}\r
\r
struct video_decoder::implementation : boost::noncopyable\r
-{ \r
+{\r
+ input& input_;\r
std::shared_ptr<SwsContext> sws_context_;\r
const std::shared_ptr<core::frame_factory> frame_factory_;\r
AVCodecContext& codec_context_;\r
const int height_;\r
const PixelFormat pix_fmt_;\r
core::pixel_format_desc desc_;\r
- std::deque<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
- : frame_factory_(frame_factory)\r
- , codec_context_(codec_context)\r
+ explicit implementation(input& input, const safe_ptr<core::frame_factory>& frame_factory) \r
+ : input_(input)\r
+ , frame_factory_(frame_factory)\r
+ , codec_context_(*input_.get_video_codec_context())\r
, width_(codec_context_.width)\r
, height_(codec_context_.height)\r
, pix_fmt_(codec_context_.pix_fmt)\r
}\r
}\r
\r
- void push(const std::shared_ptr<AVPacket>& video_packet)\r
- { \r
+ std::deque<std::pair<int, safe_ptr<core::write_frame>>> receive()\r
+ {\r
+ std::deque<std::pair<int, safe_ptr<core::write_frame>>> result;\r
+ \r
+ std::shared_ptr<AVPacket> pkt;\r
+ for(int n = 0; n < 32 && result.empty() && input_.try_pop_video_packet(pkt); ++n) \r
+ result = decode(pkt);\r
+\r
+ return result;\r
+ }\r
+\r
+ std::deque<std::pair<int, safe_ptr<core::write_frame>>> decode(const std::shared_ptr<AVPacket>& video_packet)\r
+ { \r
+ std::deque<std::pair<int, safe_ptr<core::write_frame>>> result;\r
+\r
if(!video_packet)\r
{ \r
avcodec_flush_buffers(&codec_context_);\r
frame_number_ = 0;\r
- return;\r
+ return result;\r
}\r
\r
safe_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
}\r
\r
if(frame_finished != 0) \r
- frames_.push_back(make_write_frame(decoded_frame));\r
+ result.push_back(std::make_pair(frame_number_++, make_write_frame(decoded_frame)));\r
+\r
+ return result;\r
}\r
\r
safe_ptr<core::write_frame> make_write_frame(safe_ptr<AVFrame> decoded_frame)\r
\r
return write;\r
}\r
-\r
- bool empty() const\r
- {\r
- return frames_.empty();\r
- }\r
-\r
- safe_ptr<core::write_frame> front()\r
- {\r
- return frames_.front();\r
- }\r
-\r
- void pop()\r
- {\r
- ++frame_number_;\r
- frames_.pop_front();\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(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
+video_decoder::video_decoder(input& input, const safe_ptr<core::frame_factory>& frame_factory) : impl_(new implementation(input, frame_factory)){}\r
+std::deque<std::pair<int, safe_ptr<core::write_frame>>> video_decoder::receive(){return impl_->receive();}\r
\r
}
\ No newline at end of file
\r
#include <common/memory/safe_ptr.h>\r
\r
-struct AVCodecContext;\r
-\r
+#include "../input.h"\r
namespace caspar {\r
\r
namespace core {\r
class video_decoder : boost::noncopyable\r
{\r
public:\r
- explicit video_decoder(AVCodecContext& codec_context, const safe_ptr<core::frame_factory>& frame_factory);\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
+ explicit video_decoder(input& input, const safe_ptr<core::frame_factory>& frame_factory);\r
+ std::deque<std::pair<int, safe_ptr<core::write_frame>>> receive(); \r
\r
private:\r
struct implementation;\r
return;\r
\r
s->thread_opaque = nullptr;\r
-\r
+ \r
CASPAR_LOG(info) << "Released ffmpeg tbb context.";\r
}\r
\r
std::getline(std::wcin, wcmd); // TODO: It's blocking...\r
\r
is_running = wcmd != L"exit" && wcmd != L"q";\r
- if(wcmd.substr(0, 2) == L"12")\r
+ if(wcmd.substr(0, 2) == L"11")\r
{\r
- wcmd = L"LOADBG 1-1 A LOOP \r\nPLAY 1-1\r\n";\r
- amcp.Parse(wcmd.c_str(), wcmd.length(), dummy);\r
- wcmd = L"LOADBG 1-2 DV LOOP AUTOPLAY\r\nnPLAY 1-1\r\n";\r
- amcp.Parse(wcmd.c_str(), wcmd.length(), dummy);\r
- wcmd = L"MIXER 1-1 VIDEO FIX_RECT 0.0 0.0 0.5 0.5\r\n";\r
- amcp.Parse(wcmd.c_str(), wcmd.length(), dummy);\r
- wcmd = L"MIXER 1-2 VIDEO FIX_RECT 0.5 0.0 0.5 0.5\r\n";\r
- amcp.Parse(wcmd.c_str(), wcmd.length(), dummy);\r
+ auto file = wcmd.substr(3, wcmd.length()-1);\r
+ wcmd = L"MIXER 1 VIDEO GRID 3";\r
+ wcmd += L"\r\nPLAY 1-1 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-2 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-3 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-4 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-5 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-6 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-7 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-8 " + file + L" SLIDE 100 LOOP";\r
+ wcmd += L"\r\nPLAY 1-9 " + file + L" SLIDE 100 LOOP";\r
}\r
else if(wcmd.substr(0, 2) == L"10")\r
wcmd = L"MIXER 1-1 VIDEO CLIP_RECT 0.4 0.4 0.5 0.5";\r