X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Fvideo%2Fvideo_decoder.cpp;h=5dba7c2f7a98d9cd2e49dd333bbc9801a84740de;hb=81908c20a405191305f4f643c7a96b44e00861f8;hp=bcd3ac8acb17cc08445b9b99fdb3ae507f9f0f24;hpb=68dedb12c98c7551d8f827d8f1adc489cbc05f09;p=casparcg diff --git a/modules/ffmpeg/producer/video/video_decoder.cpp b/modules/ffmpeg/producer/video/video_decoder.cpp index bcd3ac8ac..5dba7c2f7 100644 --- a/modules/ffmpeg/producer/video/video_decoder.cpp +++ b/modules/ffmpeg/producer/video/video_decoder.cpp @@ -20,24 +20,20 @@ #include "../../stdafx.h" #include "video_decoder.h" + #include "../util.h" #include "../filter/filter.h" #include "../../ffmpeg_error.h" #include "../../tbb_avcodec.h" -#include - -#include -#include -#include #include #include -#include -#include +#include +#include -#include +#include #if defined(_MSC_VER) #pragma warning (push) @@ -45,192 +41,178 @@ #endif extern "C" { - #define __STDC_CONSTANT_MACROS - #define __STDC_LIMIT_MACROS - #include #include + #include } #if defined(_MSC_VER) #pragma warning (pop) #endif namespace caspar { - + struct video_decoder::implementation : boost::noncopyable { const safe_ptr frame_factory_; std::shared_ptr codec_context_; int index_; - core::video_mode::type mode_; - std::queue> packet_buffer_; + std::queue> packets_; - std::unique_ptr filter_; + filter filter_; double fps_; int64_t nb_frames_; + + size_t width_; + size_t height_; + public: - explicit implementation(const std::shared_ptr& context, const safe_ptr& frame_factory, const std::wstring& filter) + explicit implementation(const safe_ptr& context, const safe_ptr& frame_factory, const std::wstring& filter) : frame_factory_(frame_factory) - , mode_(core::video_mode::invalid) - , filter_(filter.empty() ? nullptr : new caspar::filter(filter)) + , filter_(filter) , fps_(frame_factory_->get_video_format_desc().fps) , nb_frames_(0) + , width_(0) + , height_(0) { - AVCodec* dec; - index_ = av_find_best_stream(context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 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; - if(index_ < 0) - return; + // Some files give an invalid time_base numerator, try to fix it. + + fix_meta_data(*context); - int errn = tbb_avcodec_open(context->streams[index_]->codec, dec); - if(errn < 0) - return; - - codec_context_.reset(context->streams[index_]->codec, tbb_avcodec_close); - - // 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); + nb_frames_ = context->streams[index_]->nb_frames; - fps_ = static_cast(codec_context_->time_base.den) / static_cast(codec_context_->time_base.num); - if(double_rate(filter)) - fps_ *= 2; + 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."; + } } void push(const std::shared_ptr& packet) { - if(!codec_context_) - return; - if(packet && packet->stream_index != index_) return; - packet_buffer_.push(packet); + packets_.push(packet); } std::vector> poll() { std::vector> result; + if(packets_.empty()) + return result; + if(!codec_context_) - { - std::shared_ptr frame(avcodec_alloc_frame(), av_free); - frame->data[0] = nullptr; - result.push_back(frame); - } - else if(!packet_buffer_.empty()) - { - std::shared_ptr frame; + return empty_poll(); - auto packet = packet_buffer_.front(); + auto packet = packets_.front(); + + if(packet) + { + BOOST_FOREACH(auto& frame, decode(*packet)) + boost::range::push_back(result, filter_.execute(frame)); - bool eof = false; - - if(packet) - { - frame = decode(*packet); - if(packet->size == 0) - packet_buffer_.pop(); - } - else + if(packet->size == 0) + packets_.pop(); + } + else + { + if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) { - if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) - { - AVPacket pkt; - av_init_packet(&pkt); - pkt.data = nullptr; - pkt.size = 0; - frame = decode(pkt); - } - - if(!frame) - { - packet_buffer_.pop(); - avcodec_flush_buffers(codec_context_.get()); - eof = true; - } - } - - CASPAR_VERIFY(!frame || frame->repeat_pict == 0); + AVPacket pkt; + av_init_packet(&pkt); + pkt.data = nullptr; + pkt.size = 0; - std::vector> av_frames; - - if(filter_) - av_frames = filter_->execute(frame); - else if(frame) - av_frames.push_back(make_safe(frame)); - - BOOST_FOREACH(auto& frame, av_frames) - result.push_back(frame); + BOOST_FOREACH(auto& frame, decode(pkt)) + boost::range::push_back(result, filter_.execute(frame)); + } - if(eof) + if(result.empty()) + { + packets_.pop(); + avcodec_flush_buffers(codec_context_.get()); result.push_back(nullptr); + } } return result; } - std::shared_ptr decode(AVPacket& pkt) + 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 ret = avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, &pkt); + THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, &pkt), "[video_decocer]"); - if(ret < 0) - { - BOOST_THROW_EXCEPTION( - invalid_operation() << - msg_info(av_error_str(ret)) << - boost::errinfo_api_function("avcodec_decode_video") << - boost::errinfo_errno(AVUNERROR(ret))); - } - - // 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 + // 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) - { - if(decoded_frame->repeat_pict != 0) - CASPAR_LOG(warning) << "video_decoder: repeat_pict not implemented."; - return decoded_frame; - } + if(frame_finished == 0) + return std::vector>(); - return nullptr; + 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 !codec_context_ || !packet_buffer_.empty(); + return !packets_.empty(); } - core::video_mode::type mode() - { - if(!codec_context_) - return frame_factory_->get_video_format_desc().mode; - - return mode_; - } - double fps() const { return fps_; } }; -video_decoder::video_decoder(const std::shared_ptr& context, const safe_ptr& frame_factory, const std::wstring& filter) : impl_(new implementation(context, frame_factory, filter)){} +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();} -core::video_mode::type video_decoder::mode(){return impl_->mode();} 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