X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Fvideo%2Fvideo_decoder.cpp;h=5dba7c2f7a98d9cd2e49dd333bbc9801a84740de;hb=81908c20a405191305f4f643c7a96b44e00861f8;hp=68c8d11d98b39db9c786c7df980f212cc8f9d978;hpb=a218d0ca116970342713f777b28eb22c34cd14d8;p=casparcg diff --git a/modules/ffmpeg/producer/video/video_decoder.cpp b/modules/ffmpeg/producer/video/video_decoder.cpp index 68c8d11d9..5dba7c2f7 100644 --- a/modules/ffmpeg/producer/video/video_decoder.cpp +++ b/modules/ffmpeg/producer/video/video_decoder.cpp @@ -1,17 +1,39 @@ +/* +* copyright (c) 2010 Sveriges Television AB +* +* This file is part of CasparCG. +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +*/ #include "../../stdafx.h" #include "video_decoder.h" -#include +#include "../util.h" +#include "../filter/filter.h" -#include +#include "../../ffmpeg_error.h" +#include "../../tbb_avcodec.h" -#include #include +#include -#include +#include +#include -#include +#include #if defined(_MSC_VER) #pragma warning (push) @@ -19,9 +41,7 @@ #endif extern "C" { - #define __STDC_CONSTANT_MACROS - #define __STDC_LIMIT_MACROS - #include + #include #include } #if defined(_MSC_VER) @@ -30,147 +50,169 @@ extern "C" namespace caspar { -core::pixel_format::type get_pixel_format(PixelFormat pix_fmt) +struct video_decoder::implementation : boost::noncopyable { - switch(pix_fmt) - { - case PIX_FMT_BGRA: return core::pixel_format::bgra; - case PIX_FMT_ARGB: return core::pixel_format::argb; - case PIX_FMT_RGBA: return core::pixel_format::rgba; - case PIX_FMT_ABGR: return core::pixel_format::abgr; - case PIX_FMT_YUV444P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV422P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV420P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV411P: return core::pixel_format::ycbcr; - case PIX_FMT_YUV410P: return core::pixel_format::ycbcr; - case PIX_FMT_YUVA420P: return core::pixel_format::ycbcra; - default: return core::pixel_format::invalid; - } -} + const safe_ptr frame_factory_; + std::shared_ptr codec_context_; + int index_; -core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height) -{ - // Get linesizes - AVPicture dummy_pict; - avpicture_fill(&dummy_pict, nullptr, pix_fmt, width, height); + std::queue> packets_; - core::pixel_format_desc desc; - desc.pix_fmt = get_pixel_format(pix_fmt); - - switch(desc.pix_fmt) + filter filter_; + + double fps_; + int64_t nb_frames_; + + size_t width_; + size_t height_; + +public: + 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) { - case core::pixel_format::bgra: - case core::pixel_format::argb: - case core::pixel_format::rgba: - case core::pixel_format::abgr: + try { - desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 4)); - return desc; + 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. + + fix_meta_data(*context); + + fps_ = static_cast(codec_context_->time_base.den) / static_cast(codec_context_->time_base.num); + nb_frames_ = context->streams[index_]->nb_frames; + + 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."; } - case core::pixel_format::ycbcr: - case core::pixel_format::ycbcra: - { - // Find chroma height - size_t size2 = dummy_pict.data[2] - dummy_pict.data[1]; - size_t h2 = size2/dummy_pict.linesize[1]; - - desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1)); - desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[1], h2, 1)); - desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[2], h2, 1)); - - if(desc.pix_fmt == core::pixel_format::ycbcra) - desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[3], height, 1)); - return desc; - } - default: - desc.pix_fmt = core::pixel_format::invalid; - return desc; } -} -struct video_decoder::implementation : boost::noncopyable -{ - std::shared_ptr frame_factory_; - std::shared_ptr sws_context_; + void push(const std::shared_ptr& packet) + { + if(packet && packet->stream_index != index_) + return; - AVCodecContext* codec_context_; + packets_.push(packet); + } - const int width_; - const int height_; - const PixelFormat pix_fmt_; - core::pixel_format_desc desc_; + std::vector> poll() + { + std::vector> result; -public: - explicit implementation(AVCodecContext* codec_context, const safe_ptr& frame_factory) - : frame_factory_(frame_factory) - , codec_context_(codec_context) - , width_(codec_context_->width) - , height_(codec_context_->height) - , pix_fmt_(codec_context_->pix_fmt) - , desc_(get_pixel_format_desc(pix_fmt_, width_, height_)) - { - double frame_time = static_cast(codec_context_->time_base.num) / static_cast(codec_context_->time_base.den); - double format_frame_time = 1.0/frame_factory->get_video_format_desc().fps; - if(abs(frame_time - format_frame_time) > 0.0001) - BOOST_THROW_EXCEPTION(file_read_error() << msg_info("Invalid video framerate.") << arg_value_info(boost::lexical_cast(frame_time))); + if(packets_.empty()) + return result; + + if(!codec_context_) + return empty_poll(); - if(desc_.pix_fmt == core::pixel_format::invalid) + 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 { - CASPAR_LOG(warning) << "Hardware accelerated color transform not supported."; - - desc_ = get_pixel_format_desc(PIX_FMT_BGRA, width_, height_); - double param; - sws_context_.reset(sws_getContext(width_, height_, pix_fmt_, width_, height_, PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, ¶m), sws_freeContext); - if(!sws_context_) - BOOST_THROW_EXCEPTION(operation_failed() << - msg_info("Could not create software scaling context.") << - boost::errinfo_api_function("sws_getContext")); + 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; } - - safe_ptr execute(const aligned_buffer& video_packet) + + std::vector> empty_poll() { - safe_ptr decoded_frame(avcodec_alloc_frame(), av_free); + 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 result = avcodec_decode_video(codec_context_, decoded_frame.get(), &frame_finished, video_packet.data(), video_packet.size()); - - if(result < 0) - BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("avcodec_decode_video failed")); + THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, &pkt), "[video_decocer]"); - auto write = frame_factory_->create_frame(desc_); - if(sws_context_ == nullptr) - { - tbb::parallel_for(0, static_cast(desc_.planes.size()), 1, [&](int n) - { - auto plane = desc_.planes[n]; - auto result = write->image_data(n).begin(); - auto decoded = decoded_frame->data[n]; - auto decoded_linesize = decoded_frame->linesize[n]; - - tbb::parallel_for(0, static_cast(desc_.planes[n].height), 1, [&](int y) - { - std::copy_n(decoded + y*decoded_linesize, plane.linesize, result + y*plane.linesize); - }); - }); - } - else - { - safe_ptr av_frame(avcodec_alloc_frame(), av_free); - avcodec_get_frame_defaults(av_frame.get()); - avpicture_fill(reinterpret_cast(av_frame.get()), write->image_data().begin(), PIX_FMT_BGRA, width_, height_); - - sws_scale(sws_context_.get(), decoded_frame->data, decoded_frame->linesize, 0, height_, av_frame->data, av_frame->linesize); - } + // 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(codec_context_->codec_id == CODEC_ID_DVVIDEO && frame_factory_->get_video_format_desc().mode == core::video_mode::upper) - write->get_image_transform().set_fill_translation(0.0f, 1.0/static_cast(height_)); + if(frame_finished == 0) + return std::vector>(); - return write; + 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(AVCodecContext* codec_context, const safe_ptr& frame_factory) : impl_(new implementation(codec_context, frame_factory)){} -safe_ptr video_decoder::execute(const aligned_buffer& video_packet){return impl_->execute(video_packet);} - +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