X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fffmpeg%2Fffmpeg_producer.cpp;h=65f68c479b14919f1dbfeada75b5bdfcd3a66189;hb=90f0fc83b25565df07d052209f2312ea9a02f785;hp=718f629f9de61703e957cde704194e964fb879f5;hpb=822ad9fff5882a006c3de7448082da7d1a4bf489;p=casparcg diff --git a/core/producer/ffmpeg/ffmpeg_producer.cpp b/core/producer/ffmpeg/ffmpeg_producer.cpp index 718f629f9..65f68c479 100644 --- a/core/producer/ffmpeg/ffmpeg_producer.cpp +++ b/core/producer/ffmpeg/ffmpeg_producer.cpp @@ -2,192 +2,178 @@ #include "ffmpeg_producer.h" -#if defined(_MSC_VER) -#pragma warning (push) -#pragma warning (disable : 4244) -#endif - -extern "C" -{ - #define __STDC_CONSTANT_MACROS - #define __STDC_LIMIT_MACROS - #include - #include - #include - #include -} - -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - #include "input.h" - #include "audio/audio_decoder.h" #include "video/video_decoder.h" -#include "video/video_decoder.h" -#include "../../format/video_format.h" -#include "../../processor/transform_frame.h" -#include "../../processor/draw_frame.h" -#include "../../../common/utility/scope_exit.h" -#include "../../server.h" +#include +#include +#include +#include +#include -#include -#include -#include +#include -#include -#include -#include -#include +#include #include - -using namespace boost::assign; +#include namespace caspar { namespace core { namespace ffmpeg{ -struct ffmpeg_producer_impl +struct ffmpeg_producer : public frame_producer { -public: - ffmpeg_producer_impl(const std::wstring& filename, const std::vector& params) : filename_(filename), last_frame_(transform_frame(draw_frame::empty())), underrun_count_(0), - input_(filename), video_decoder_(input_.get_video_codec_context().get()), audio_decoder_(input_.get_audio_codec_context().get()) - { - input_.set_loop(std::find(params.begin(), params.end(), L"LOOP") != params.end()); + const std::wstring filename_; + const bool loop_; + printer parent_printer_; + + std::shared_ptr graph_; + timer perf_timer_; + + std::unique_ptr audio_decoder_; + std::unique_ptr video_decoder_; - auto seek = std::find(params.begin(), params.end(), L"SEEK"); - if(seek != params.end() && ++seek != params.end()) - { - if(!input_.seek(boost::lexical_cast(*seek))) - CASPAR_LOG(warning) << "Failed to seek file: " << filename_ << "to frame" << *seek; - } - } + std::deque> video_frame_channel_; + std::deque> audio_chunk_channel_; - void initialize(const safe_ptr& frame_processor) + std::queue> ouput_channel_; + + safe_ptr last_frame_; + std::shared_ptr frame_factory_; + + std::unique_ptr input_; +public: + explicit ffmpeg_producer(const std::wstring& filename, bool loop) + : filename_(filename) + , loop_(loop) + , last_frame_(draw_frame(draw_frame::empty())) + + { + graph_ = diagnostics::create_graph(boost::bind(&ffmpeg_producer::print, this)); + graph_->guide("frame-time", 0.5); + graph_->set_color("frame-time", diagnostics::color(1.0f, 0.0f, 0.0f)); + } + + virtual void initialize(const safe_ptr& frame_factory) { - format_desc_ = frame_processor->get_video_format_desc(); - video_decoder_.initialize(frame_processor); + frame_factory_ = frame_factory; + input_.reset(new input(safe_ptr(graph_), filename_, loop_, std::bind(&ffmpeg_producer::print, this))); + video_decoder_.reset(input_->get_video_codec_context().get() ? new video_decoder(input_->get_video_codec_context().get(), frame_factory) : nullptr); + audio_decoder_.reset(input_->get_audio_codec_context().get() ? new audio_decoder(input_->get_audio_codec_context().get(), frame_factory->get_video_format_desc().fps) : nullptr); } - safe_ptr receive() + virtual void set_parent_printer(const printer& parent_printer) + { + parent_printer_ = parent_printer; + } + + virtual safe_ptr receive() { - while(ouput_channel_.empty() && !input_.is_eof()) + perf_timer_.reset(); + + while(ouput_channel_.empty() && !input_->is_eof()) { aligned_buffer video_packet; - if(video_frame_channel_.size() < 3) - video_packet = input_.get_video_packet(); + if(video_frame_channel_.size() < 3 && video_decoder_) + video_packet = input_->get_video_packet(); aligned_buffer audio_packet; - if(audio_chunk_channel_.size() < 3) - audio_packet = input_.get_audio_packet(); + if(audio_chunk_channel_.size() < 3 && audio_decoder_) + audio_packet = input_->get_audio_packet(); tbb::parallel_invoke( [&] { // Video Decoding and Scaling - if(!video_packet.empty()) + if(!video_packet.empty() && video_decoder_) { - auto frame = video_decoder_.execute(video_packet); - video_frame_channel_.push_back(std::move(frame)); + try + { + auto frame = video_decoder_->execute(video_packet); + frame->tag(reinterpret_cast(this)); + video_frame_channel_.push_back(std::move(frame)); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + video_decoder_.reset(); + CASPAR_LOG(warning) << print() << " removed video-stream."; + } } }, [&] { // Audio Decoding - if(!audio_packet.empty()) + if(!audio_packet.empty() && audio_decoder_) { - auto chunks = audio_decoder_.execute(audio_packet); - audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end()); + try + { + auto chunks = audio_decoder_->execute(audio_packet); + audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end()); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + audio_decoder_.reset(); + CASPAR_LOG(warning) << print() << " removed audio-stream."; + } } }); - while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || input_.get_audio_codec_context() == nullptr)) + while((!video_frame_channel_.empty() || !video_decoder_) && (!audio_chunk_channel_.empty() || !audio_decoder_)) { - std::vector audio_data; - if(input_.get_audio_codec_context() != nullptr) + std::shared_ptr frame; + + if(video_decoder_) { - audio_data = std::move(audio_chunk_channel_.front()); + frame = video_frame_channel_.front(); + video_frame_channel_.pop_front(); + } + + if(audio_decoder_) + { + if(!frame) + { + frame = frame_factory_->create_frame(1, 1); + std::fill(frame->image_data().begin(), frame->image_data().end(), 0); + } + + frame->audio_data() = std::move(audio_chunk_channel_.front()); audio_chunk_channel_.pop_front(); } - auto write = std::move(video_frame_channel_.front()); - write->audio_data() = std::move(audio_data); - auto transform = transform_frame(write); - video_frame_channel_.pop_front(); - - // TODO: Make generic for all formats and modes. - if(input_.get_video_codec_context()->codec_id == CODEC_ID_DVVIDEO) // Move up one field - transform.translate(0.0f, 1.0/static_cast(format_desc_.height)); - - ouput_channel_.push(std::move(transform)); + ouput_channel_.push(safe_ptr(frame)); } - if(ouput_channel_.empty() && video_packet.empty() && audio_packet.empty()) - { - if(underrun_count_++ == 0) - CASPAR_LOG(warning) << print() << "### Started File read underrun."; - - return last_frame_; - } - else if(underrun_count_ > 0) - { - CASPAR_LOG(trace) << print() << "### Ended file read underrun with " << underrun_count_ << " ticks."; - underrun_count_ = 0; - } + if(ouput_channel_.empty() && video_packet.empty() && audio_packet.empty()) + return last_frame_; } + + graph_->update("frame-time", static_cast(perf_timer_.elapsed()/frame_factory_->get_video_format_desc().interval*0.5)); auto result = last_frame_; if(!ouput_channel_.empty()) { result = std::move(ouput_channel_.front()); - last_frame_ = transform_frame(result); - last_frame_->audio_volume(0.0); // last_frame should not have audio + last_frame_ = draw_frame(result); + last_frame_->get_audio_transform().set_gain(0.0); // last_frame should not have audio ouput_channel_.pop(); } - else if(input_.is_eof()) + else if(input_->is_eof()) return draw_frame::eof(); return result; } - std::wstring print() const + virtual std::wstring print() const { - return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]"; + return (parent_printer_ ? parent_printer_() + L"/" : L"") + L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]"; } - - size_t underrun_count_; - - input input_; - audio_decoder audio_decoder_; - video_decoder video_decoder_; - - std::deque> video_frame_channel_; - std::deque> audio_chunk_channel_; - - std::queue> ouput_channel_; - - const std::wstring filename_; - - safe_ptr last_frame_; - - video_format_desc format_desc_; -}; - -class ffmpeg_producer : public frame_producer -{ -public: - ffmpeg_producer(const std::wstring& filename, const std::vector& params) : impl_(new ffmpeg_producer_impl(filename, params)){} - ffmpeg_producer(ffmpeg_producer&& other) : impl_(std::move(other.impl_)){} - virtual safe_ptr receive(){return impl_->receive();} - virtual void initialize(const safe_ptr& frame_processor){impl_->initialize(frame_processor);} - virtual std::wstring print() const{return impl_->print();} -private: - std::shared_ptr impl_; }; -safe_ptr create_ffmpeg_producer(const std::vector& params) +safe_ptr create_ffmpeg_producer(const std::vector& params) { - static const std::vector extensions = list_of(L"mpg")(L"avi")(L"mov")(L"dv")(L"wav")(L"mp3")(L"mp4")(L"f4v")(L"flv"); - std::wstring filename = server::media_folder() + L"\\" + params[0]; + static const std::vector extensions = boost::assign::list_of + (L"mpg")(L"mpeg")(L"avi")(L"mov")(L"qt")(L"webm")(L"dv")(L"mp4")(L"f4v")(L"flv")(L"mkv")(L"mka")(L"wmw")(L"wma")(L"ogg")(L"divx")(L"wav")(L"mp3"); + std::wstring filename = env::media_folder() + L"\\" + params[0]; auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool { @@ -196,14 +182,11 @@ safe_ptr create_ffmpeg_producer(const std::vector if(ext == extensions.end()) return frame_producer::empty(); - - static boost::once_flag av_register_all_flag = BOOST_ONCE_INIT; - boost::call_once(av_register_all, av_register_all_flag); - - static boost::once_flag avcodec_init_flag = BOOST_ONCE_INIT; - boost::call_once(avcodec_init, avcodec_init_flag); - return make_safe(filename + L"." + *ext, params); + std::wstring path = filename + L"." + *ext; + bool loop = std::find(params.begin(), params.end(), L"LOOP") != params.end(); + + return make_safe(path, loop); } }}} \ No newline at end of file