X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Fvideo%2Fvideo_decoder.cpp;h=d78361f678db523817fdbb35578c523be52c463f;hb=0d225c5442d5a127b0eefd08c1cf723a37357211;hp=e78fe8619be72faec4a8a02aaa31c6deb5ac2f68;hpb=c52849930dadae4f06a5da7aba212c726503c6d7;p=casparcg diff --git a/modules/ffmpeg/producer/video/video_decoder.cpp b/modules/ffmpeg/producer/video/video_decoder.cpp index e78fe8619..d78361f67 100644 --- a/modules/ffmpeg/producer/video/video_decoder.cpp +++ b/modules/ffmpeg/producer/video/video_decoder.cpp @@ -20,22 +20,19 @@ #include "../../stdafx.h" #include "video_decoder.h" -#include "../util.h" -#include "../../ffmpeg_error.h" +#include "../util.h" #include "../filter/filter.h" -#include +#include "../../ffmpeg_error.h" +#include "../../tbb_avcodec.h" -#include -#include -#include #include #include -#include +#include -#include +#include #if defined(_MSC_VER) #pragma warning (push) @@ -43,10 +40,8 @@ #endif extern "C" { - #define __STDC_CONSTANT_MACROS - #define __STDC_LIMIT_MACROS - #include #include + #include } #if defined(_MSC_VER) #pragma warning (pop) @@ -56,119 +51,169 @@ namespace caspar { struct video_decoder::implementation : boost::noncopyable { - input& input_; const safe_ptr frame_factory_; - AVCodecContext& codec_context_; - size_t frame_number_; + std::shared_ptr codec_context_; + int index_; - std::shared_ptr filter_; - size_t filter_delay_; - int eof_count_; + std::queue> packets_; - std::string filter_str_; + filter filter_; + + double fps_; + int64_t nb_frames_; + + size_t width_; + size_t height_; public: - explicit implementation(input& input, const safe_ptr& frame_factory, const std::string& filter_str) - : input_(input) - , frame_factory_(frame_factory) - , codec_context_(*input_.get_video_codec_context()) - , frame_number_(0) - , filter_(filter_str.empty() ? nullptr : new filter(filter_str)) - , filter_delay_(1) - , filter_str_(filter_str) - , eof_count_(std::numeric_limits::max()) + explicit implementation(const safe_ptr& context, const safe_ptr& frame_factory, const std::wstring& filter) + : frame_factory_(frame_factory) + , filter_(filter) + , fps_(frame_factory_->get_video_format_desc().fps) + , nb_frames_(0) + , width_(0) + , height_(0) { + try + { + AVCodec* dec; + index_ = THROW_ON_ERROR2(av_find_best_stream(context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0), "[video_decoder]"); + + THROW_ON_ERROR2(tbb_avcodec_open(context->streams[index_]->codec, dec), "[video_decoder]"); + + codec_context_.reset(context->streams[index_]->codec, tbb_avcodec_close); + + CASPAR_LOG(debug) << "[video_decoder] " << context->streams[index_]->codec->codec->long_name; + + // Some files give an invalid time_base numerator, try to fix it. + if(codec_context_ && codec_context_->time_base.num == 1) + codec_context_->time_base.num = static_cast(std::pow(10.0, static_cast(std::log10(static_cast(codec_context_->time_base.den)))-1)); + + nb_frames_ = context->streams[index_]->nb_frames; + if(nb_frames_ == 0) + nb_frames_ = context->streams[index_]->duration;// * context->streams[index_]->time_base.den; + + fps_ = static_cast(codec_context_->time_base.den) / static_cast(codec_context_->time_base.num); + if(double_rate(filter)) + fps_ *= 2; + + width_ = codec_context_->width; + height_ = codec_context_->height; + } + catch(...) + { + index_ = THROW_ON_ERROR2(av_find_best_stream(context.get(), AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0), "[video_decoder]"); + + CASPAR_LOG_CURRENT_EXCEPTION(); + CASPAR_LOG(warning) << "[video_decoder] Failed to open video-stream. Running without video."; + } } - std::deque>> receive() + void push(const std::shared_ptr& packet) { - std::deque>> result; - - std::shared_ptr pkt; - for(int n = 0; n < 32 && result.empty() && input_.try_pop_video_packet(pkt); ++n) - boost::range::push_back(result, decode(pkt)); + if(packet && packet->stream_index != index_) + return; - return result; + packets_.push(packet); } - std::deque>> decode(const std::shared_ptr& video_packet) - { - std::deque>> result; + std::vector> poll() + { + std::vector> result; - if(!video_packet) // eof - { - eof_count_ = frame_number_ + (filter_ ? filter_delay_ : 0); - avcodec_flush_buffers(&codec_context_); + if(packets_.empty()) return result; - } - - frame_number_ = frame_number_ % eof_count_; - - const void* tag = this; - if(filter_) - { - std::shared_ptr frame; - - tbb::parallel_invoke( - [&] - { - frame = decode_frame(video_packet); - }, - [&] - { - if(filter_->is_ready()) - { - boost::range::transform(filter_->poll(), std::back_inserter(result), [&](const safe_ptr& frame) - { - return std::make_pair(frame_number_, make_write_frame(tag, frame, frame_factory_)); - }); - - if(!result.empty()) - ++frame_number_; - else - ++filter_delay_; - } - }); - - if(frame) - filter_->push(make_safe(frame)); + if(!codec_context_) + return empty_poll(); + + auto packet = packets_.front(); + + if(packet) + { + BOOST_FOREACH(auto& frame, decode(*packet)) + boost::range::push_back(result, filter_.execute(frame)); + + if(packet->size == 0) + packets_.pop(); } else { - auto frame = decode_frame(video_packet); - - if(frame) - result.push_back(std::make_pair(frame_number_++, make_write_frame(tag, make_safe(frame), frame_factory_))); + if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) + { + AVPacket pkt; + av_init_packet(&pkt); + pkt.data = nullptr; + pkt.size = 0; + + BOOST_FOREACH(auto& frame, decode(pkt)) + boost::range::push_back(result, filter_.execute(frame)); + } + + if(result.empty()) + { + packets_.pop(); + avcodec_flush_buffers(codec_context_.get()); + result.push_back(nullptr); + } } - + return result; } - - std::shared_ptr decode_frame(const std::shared_ptr& video_packet) + + std::vector> empty_poll() + { + auto packet = packets_.front(); + packets_.pop(); + + if(!packet) + return boost::assign::list_of(nullptr); + + std::shared_ptr frame(avcodec_alloc_frame(), av_free); + frame->data[0] = nullptr; + + return boost::assign::list_of(frame); + } + + std::vector> decode(AVPacket& pkt) { std::shared_ptr decoded_frame(avcodec_alloc_frame(), av_free); int frame_finished = 0; - const int errn = avcodec_decode_video2(&codec_context_, decoded_frame.get(), &frame_finished, video_packet.get()); + THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, &pkt), "[video_decocer]"); - if(errn < 0) - { - BOOST_THROW_EXCEPTION( - invalid_operation() << - msg_info(av_error_str(errn)) << - boost::errinfo_api_function("avcodec_decode_video") << - boost::errinfo_errno(AVUNERROR(errn))); - } + // If a decoder consumes less then the whole packet then something is wrong + // that might be just harmless padding at the end, or a problem with the + // AVParser or demuxer which puted more then one frame in a AVPacket. + pkt.data = nullptr; + pkt.size = 0; - if(frame_finished == 0) - decoded_frame = nullptr; + if(frame_finished == 0) + return std::vector>(); - return decoded_frame; + if(decoded_frame->repeat_pict % 2 > 0) + CASPAR_LOG(warning) << "[video_decoder]: Field repeat_pict not implemented."; + + return std::vector>(1 + decoded_frame->repeat_pict/2, decoded_frame); + } + + bool ready() const + { + return !packets_.empty(); + } + + double fps() const + { + return fps_; } }; -video_decoder::video_decoder(input& input, const safe_ptr& frame_factory, const std::string& filter_str) : impl_(new implementation(input, frame_factory, filter_str)){} -std::deque>> video_decoder::receive(){return impl_->receive();} - +video_decoder::video_decoder(const safe_ptr& context, const safe_ptr& frame_factory, const std::wstring& filter) : impl_(new implementation(context, frame_factory, filter)){} +void video_decoder::push(const std::shared_ptr& packet){impl_->push(packet);} +std::vector> video_decoder::poll(){return impl_->poll();} +bool video_decoder::ready() const{return impl_->ready();} +double video_decoder::fps() const{return impl_->fps();} +int64_t video_decoder::nb_frames() const{return impl_->nb_frames_;} +size_t video_decoder::width() const{return impl_->width_;} +size_t video_decoder::height() const{return impl_->height_;} } \ No newline at end of file