typedef std::vector<short, tbb::cache_aligned_allocator<short>> buffer;\r
\r
AVCodecContext* codec_context_;\r
+ \r
+ const core::video_format_desc format_desc_;\r
\r
- buffer audio_buffer_; \r
buffer current_chunk_;\r
\r
- const size_t audio_frame_size_;\r
-\r
- static const size_t SAMPLE_RATE = 48000;\r
- static const size_t N_CHANNELS = 2;\r
-\r
public:\r
- explicit implementation(AVCodecContext* codec_context, double fps) \r
+ explicit implementation(AVCodecContext* codec_context, const core::video_format_desc& format_desc) \r
: codec_context_(codec_context)\r
- , audio_buffer_(4*SAMPLE_RATE*2+FF_INPUT_BUFFER_PADDING_SIZE/2)\r
- , audio_frame_size_(static_cast<size_t>(static_cast<double>(SAMPLE_RATE) / fps) * N_CHANNELS)\r
+ , format_desc_(format_desc) \r
{\r
if(!codec_context)\r
- BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("codec_context")); \r
+ { \r
+ BOOST_THROW_EXCEPTION(\r
+ null_argument() << \r
+ arg_name_info("codec_context")); \r
+ }\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
+ { \r
+ BOOST_THROW_EXCEPTION(\r
+ file_read_error() <<\r
+ msg_info("Invalid sample-rate or number of channels.") <<\r
+ arg_value_info(boost::lexical_cast<std::string>(codec_context_->sample_rate)) << \r
+ arg_name_info("codec_context"));\r
+ }\r
}\r
\r
std::vector<std::vector<short>> execute(const std::shared_ptr<aligned_buffer>& audio_packet)\r
return std::vector<std::vector<short>>();\r
}\r
\r
- int written_bytes = audio_buffer_.size()*2 - FF_INPUT_BUFFER_PADDING_SIZE;\r
- const int errn = avcodec_decode_audio2(codec_context_, audio_buffer_.data(), &written_bytes, audio_packet->data(), audio_packet->size());\r
-\r
- if(errn < 0 || codec_context_->sample_rate != SAMPLE_RATE || codec_context_->channels != 2)\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
+ \r
+ int written_bytes = (current_chunk_.size() - s)*2 - FF_INPUT_BUFFER_PADDING_SIZE;\r
+ const int errn = avcodec_decode_audio2(codec_context_, ¤t_chunk_[s], &written_bytes, audio_packet->data(), audio_packet->size());\r
+ if(errn < 0)\r
{ \r
BOOST_THROW_EXCEPTION(\r
invalid_operation() <<\r
boost::errinfo_errno(AVUNERROR(errn)));\r
}\r
\r
- current_chunk_.insert(current_chunk_.end(), audio_buffer_.data(), audio_buffer_.data() + written_bytes/2);\r
+ current_chunk_.resize(s + written_bytes/2);\r
\r
+ const auto last = current_chunk_.end() - current_chunk_.size() % format_desc_.audio_samples_per_frame;\r
+ \r
std::vector<std::vector<short>> chunks;\r
- \r
- const auto last = current_chunk_.end() - current_chunk_.size() % audio_frame_size_;\r
-\r
- for(auto it = current_chunk_.begin(); it != last; it += audio_frame_size_) \r
- chunks.push_back(std::vector<short>(it, it + audio_frame_size_)); \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
\r
current_chunk_.erase(current_chunk_.begin(), last);\r
\r
}\r
};\r
\r
-audio_decoder::audio_decoder(AVCodecContext* codec_context, double fps) : impl_(new implementation(codec_context, fps)){}\r
+audio_decoder::audio_decoder(AVCodecContext* codec_context, const core::video_format_desc& format_desc) : impl_(new implementation(codec_context, format_desc)){}\r
std::vector<std::vector<short>> audio_decoder::execute(const std::shared_ptr<aligned_buffer>& audio_packet){return impl_->execute(audio_packet);}\r
}
\ No newline at end of file
, graph_(diagnostics::create_graph(narrow(print())))\r
, frame_factory_(frame_factory) \r
, input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop_, start_frame, end_frame)\r
- , video_decoder_(input_.get_video_codec_context().get() ? new video_decoder(input_.get_video_codec_context().get(), frame_factory) : nullptr)\r
- , audio_decoder_(input_.get_audio_codec_context().get() ? new audio_decoder(input_.get_audio_codec_context().get(), frame_factory->get_video_format_desc().fps) : nullptr)\r
{\r
graph_->add_guide("frame-time", 0.5);\r
graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f));\r
double format_frame_time = 1.0/frame_factory->get_video_format_desc().fps;\r
if(abs(frame_time - format_frame_time) > 0.0001)\r
CASPAR_LOG(warning) << print() << L" Invalid framerate detected. This may cause distorted audio during playback. frame-time: " << frame_time;\r
+\r
+ try\r
+ { \r
+ video_decoder_.reset(input_.get_video_codec_context().get() ? \r
+ new video_decoder(input_.get_video_codec_context().get(), frame_factory) : nullptr);\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ video_decoder_.reset();\r
+ CASPAR_LOG(warning) << print() << " removed video-stream.";\r
+ }\r
+ \r
+ try\r
+ { \r
+ audio_decoder_.reset(input_.get_audio_codec_context().get() ? \r
+ new audio_decoder(input_.get_audio_codec_context().get(), frame_factory->get_video_format_desc()) : nullptr);\r
+ }\r
+ catch(...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ audio_decoder_.reset();\r
+ CASPAR_LOG(warning) << print() << " removed audio-stream.";\r
+ } \r
+\r
+ if(!video_decoder_ && !audio_decoder_)\r
+ {\r
+ BOOST_THROW_EXCEPTION(\r
+ caspar_exception() <<\r
+ msg_info("Failed to initialize any decoder"));\r
+ }\r
}\r
\r
virtual safe_ptr<core::basic_frame> receive()\r
#include <common/diagnostics/graph.h>\r
\r
#include <tbb/concurrent_queue.h>\r
-#include <tbb/queuing_mutex.h>\r
+#include <tbb/mutex.h>\r
\r
-#include <boost/exception/error_info.hpp>\r
-#include <boost/thread/once.hpp>\r
-#include <boost/thread/thread.hpp>\r
-#include <boost/regex.hpp>\r
-\r
-#include <errno.h>\r
-#include <system_error>\r
- \r
#if defined(_MSC_VER)\r
#pragma warning (disable : 4244)\r
#endif\r
std::shared_ptr<AVCodecContext> video_codec_context_;\r
std::shared_ptr<AVCodecContext> audio_codex_context_;\r
\r
- const std::wstring filename_;\r
-\r
- bool loop_;\r
- int video_s_index_;\r
- int audio_s_index_;\r
- const int start_frame_;\r
- const int end_frame_;\r
- int eof_count_;\r
+ const std::wstring filename_;\r
+ const bool loop_;\r
+ int video_s_index_;\r
+ int audio_s_index_;\r
+ const int start_frame_;\r
+ const int end_frame_;\r
+ int eof_count_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> video_packet_buffer_;\r
tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> audio_packet_buffer_;\r
\r
- boost::condition_variable cond_;\r
- boost::mutex mutex_;\r
+ boost::condition_variable cond_;\r
+ boost::mutex mutex_;\r
\r
std::exception_ptr exception_;\r
executor executor_;\r
, eof_count_(end_frame-start_frame)\r
{ \r
if(end_frame_ > 0 && end_frame <= start_frame_)\r
+ { \r
BOOST_THROW_EXCEPTION(\r
invalid_argument() << \r
source_info(narrow(print())) << \r
msg_info("End-frame cannot be lower than start-frame.")); \r
+ }\r
\r
graph_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f));\r
graph_->set_color("seek", diagnostics::color(0.5f, 1.0f, 0.5f)); \r
format_context_.reset(weak_format_context_, av_close_input_file);\r
\r
if((errn = av_find_stream_info(format_context_.get())) < 0)\r
+ { \r
BOOST_THROW_EXCEPTION(\r
file_read_error() << \r
source_info(narrow(print())) << \r
msg_info(av_error_str(errn)) <<\r
boost::errinfo_api_function("av_find_stream_info") <<\r
boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
\r
video_codec_context_ = open_stream(CODEC_TYPE_VIDEO, video_s_index_);\r
if(!video_codec_context_)\r
if(!audio_codex_context_)\r
CASPAR_LOG(warning) << print() << " Could not open any audio stream.";\r
else\r
- fix_time_base(video_codec_context_.get());\r
+ fix_time_base(audio_codex_context_.get());\r
\r
if(!video_codec_context_ && !audio_codex_context_)\r
+ { \r
BOOST_THROW_EXCEPTION(\r
file_read_error() << \r
msg_info(av_error_str(errn)) <<\r
source_info(narrow(print())) << \r
msg_info("No video or audio codec context found.")); \r
- \r
+ }\r
+\r
if(start_frame_ != 0) \r
seek_frame(start_frame_);\r
\r
executor_.clear();\r
executor_.stop();\r
cond_.notify_all();\r
+ }\r
+ \r
+ std::shared_ptr<aligned_buffer> get_video_packet()\r
+ {\r
+ return get_packet(video_packet_buffer_);\r
+ }\r
+\r
+ std::shared_ptr<aligned_buffer> get_audio_packet()\r
+ {\r
+ return get_packet(audio_packet_buffer_);\r
+ }\r
+\r
+ bool has_packet() const\r
+ {\r
+ return !video_packet_buffer_.empty() || !audio_packet_buffer_.empty();\r
+ }\r
+ \r
+ double fps()\r
+ {\r
+ return static_cast<double>(get_default_context()->time_base.den) / static_cast<double>(get_default_context()->time_base.num);\r
+ }\r
+\r
+private:\r
+\r
+ void stop()\r
+ {\r
+ executor_.stop();\r
CASPAR_LOG(info) << print() << " Stopped.";\r
}\r
\r
- void fix_time_base(AVCodecContext* context) // Some files give an invalid numerator, try to fix it.\r
+ void fix_time_base(AVCodecContext* context) const // Some files give an invalid numerator, try to fix it.\r
{\r
if(context && context->time_base.num == 1)\r
context->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(context->time_base.den)))-1));\r
}\r
\r
- std::shared_ptr<AVCodecContext> open_stream(int codec_type, int& s_index)\r
+ std::shared_ptr<AVCodecContext> open_stream(int codec_type, int& s_index) const\r
{ \r
AVStream** streams_end = format_context_->streams+format_context_->nb_streams;\r
- AVStream** stream = std::find_if(format_context_->streams, streams_end, \r
- [&](AVStream* stream) { return stream != nullptr && stream->codec->codec_type == codec_type ;});\r
+ AVStream** stream = std::find_if(format_context_->streams, streams_end, [&](AVStream* stream) \r
+ {\r
+ return stream != nullptr && stream->codec->codec_type == codec_type;\r
+ });\r
\r
if(stream == streams_end) \r
return nullptr;\r
{\r
return video_codec_context_ ? video_codec_context_ : audio_codex_context_;\r
}\r
-\r
- bool is_eof(int errn)\r
- {\r
- if(end_frame_ != -1)\r
- return get_default_context()->frame_number > eof_count_; \r
-\r
- return errn == AVERROR_EOF || errn == AVERROR_IO;\r
- }\r
\r
void read_file()\r
{ \r
AVPacket tmp_packet;\r
safe_ptr<AVPacket> read_packet(&tmp_packet, av_free_packet); \r
\r
- auto read_frame_ret = av_read_frame(format_context_.get(), read_packet.get());\r
- if(is_eof(read_frame_ret))\r
+ const int errn = av_read_frame(format_context_.get(), read_packet.get());\r
+ if(is_eof(errn))\r
{\r
if(loop_)\r
{\r
else\r
stop();\r
}\r
- else if(read_frame_ret < 0)\r
+ else if(errn < 0)\r
{\r
BOOST_THROW_EXCEPTION(\r
invalid_operation() <<\r
- msg_info(av_error_str(read_frame_ret)) <<\r
+ msg_info(av_error_str(errn)) <<\r
source_info(narrow(print())) << \r
boost::errinfo_api_function("av_read_frame") <<\r
- boost::errinfo_errno(AVUNERROR(read_frame_ret)));\r
+ boost::errinfo_errno(AVUNERROR(errn)));\r
}\r
else\r
{\r
- auto packet = std::make_shared<aligned_buffer>(read_packet->data, read_packet->data + read_packet->size);\r
if(read_packet->stream_index == video_s_index_) \r
- video_packet_buffer_.try_push(std::move(packet)); \r
+ video_packet_buffer_.try_push(std::make_shared<aligned_buffer>(read_packet->data, read_packet->data + read_packet->size)); \r
else if(read_packet->stream_index == audio_s_index_) \r
- audio_packet_buffer_.try_push(std::move(packet)); \r
+ audio_packet_buffer_.try_push(std::make_shared<aligned_buffer>(read_packet->data, read_packet->data + read_packet->size)); \r
}\r
\r
graph_->update_value("input-buffer", static_cast<float>(video_packet_buffer_.size())/static_cast<float>(PACKET_BUFFER_COUNT)); \r
cond_.wait(lock); \r
}\r
\r
- void stop()\r
- {\r
- executor_.stop();\r
- CASPAR_LOG(info) << print() << " eof";\r
- }\r
- \r
- std::shared_ptr<aligned_buffer> get_video_packet()\r
- {\r
- return get_packet(video_packet_buffer_);\r
- }\r
-\r
- std::shared_ptr<aligned_buffer> get_audio_packet()\r
- {\r
- return get_packet(audio_packet_buffer_);\r
- }\r
-\r
- bool has_packet() const\r
- {\r
- return !video_packet_buffer_.empty() || !audio_packet_buffer_.empty();\r
- }\r
- \r
void seek_frame(int64_t frame, int flags = 0)\r
{ \r
// Convert from frames into seconds.\r
const int errn = av_seek_frame(format_context_.get(), -1, ts, flags | AVSEEK_FLAG_FRAME);\r
\r
if(errn < 0)\r
+ { \r
BOOST_THROW_EXCEPTION(\r
invalid_operation() << \r
source_info(narrow(print())) << \r
msg_info(av_error_str(errn)) <<\r
boost::errinfo_api_function("seek_frame") <<\r
boost::errinfo_errno(AVUNERROR(errn)));\r
- \r
+ }\r
+\r
// Notify decoders to flush buffers.\r
video_packet_buffer_.try_push(std::make_shared<aligned_buffer>()); \r
audio_packet_buffer_.try_push(std::make_shared<aligned_buffer>());\r
}\r
- \r
+ \r
+\r
+ bool is_eof(int errn)\r
+ {\r
+ if(end_frame_ != -1)\r
+ return get_default_context()->frame_number > eof_count_; \r
+\r
+ return errn == AVERROR_EOF || errn == AVERROR_IO;\r
+ }\r
+ \r
std::shared_ptr<aligned_buffer> get_packet(tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>>& buffer)\r
{\r
cond_.notify_all();\r
std::shared_ptr<aligned_buffer> packet;\r
return buffer.try_pop(packet) ? packet : nullptr;\r
}\r
- \r
- double fps()\r
- {\r
- return static_cast<double>(get_default_context()->time_base.den) / static_cast<double>(get_default_context()->time_base.num);\r
- }\r
\r
std::wstring print() const\r
{\r