X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Fvideo%2Fvideo_decoder.cpp;h=bc379a8c0b9378d1aab1517dbef8d9f9e2d1e1f8;hb=70235ae09df45c874e133fd4d3fcc1e6e34e993a;hp=e69c25227eef0ef7012f05f5562614645ab79624;hpb=4239f17229cb358edb361f9e97e65baf01d0e318;p=casparcg diff --git a/modules/ffmpeg/producer/video/video_decoder.cpp b/modules/ffmpeg/producer/video/video_decoder.cpp index e69c25227..bc379a8c0 100644 --- a/modules/ffmpeg/producer/video/video_decoder.cpp +++ b/modules/ffmpeg/producer/video/video_decoder.cpp @@ -1,176 +1,178 @@ -/* -* 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 "../util.h" -#include "../filter/filter.h" - -#include "../../ffmpeg_error.h" - -#include - -#if defined(_MSC_VER) -#pragma warning (push) -#pragma warning (disable : 4244) -#endif -extern "C" -{ - #include - #include -} -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - -#include -#include - -using namespace Concurrency; - -namespace caspar { namespace ffmpeg { - -struct video_decoder::implementation : public Concurrency::agent, boost::noncopyable -{ - int index_; - std::shared_ptr codec_context_; - - double fps_; - int64_t nb_frames_; - - size_t width_; - size_t height_; - bool is_progressive_; - - overwrite_buffer is_running_; - unbounded_buffer source_; - ITarget& target_; - - safe_ptr semaphore_; - -public: - explicit implementation(video_decoder::source_t& source, video_decoder::target_t& target, AVFormatContext& context) - : codec_context_(open_codec(context, AVMEDIA_TYPE_VIDEO, index_)) - , fps_(static_cast(codec_context_->time_base.den) / static_cast(codec_context_->time_base.num)) - , nb_frames_(context.streams[index_]->nb_frames) - , width_(codec_context_->width) - , height_(codec_context_->height) - , is_progressive_(true) - , source_([this](const packet_message_t& message) - { - return message->payload && message->payload->stream_index == index_; - }) - , target_(target) - , semaphore_(make_safe(1)) - { - CASPAR_LOG(debug) << "[video_decoder] " << context.streams[index_]->codec->codec->long_name; - - CASPAR_VERIFY(width_ > 0, ffmpeg_error()); - CASPAR_VERIFY(height_ > 0, ffmpeg_error()); - - Concurrency::connect(source, source_); - - start(); - } - - ~implementation() - { - send(is_running_, false); - agent::wait(this); - } - - virtual void run() - { - try - { - send(is_running_, true); - while(is_running_.value()) - { - auto message = receive(source_); - auto packet = message->payload; - - if(!packet) - continue; - - if(packet == loop_packet(index_)) - { - send(target_, make_message(loop_video())); - continue; - } - - if(packet == eof_packet(index_)) - break; - - token token(semaphore_); - std::shared_ptr decoded_frame(avcodec_alloc_frame(), [this, token](AVFrame* frame) - { - av_free(frame); - }); - - int frame_finished = 0; - THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, packet.get()), "[video_decocer]"); - - // 1 packet <=> 1 frame. - // 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(frame_finished == 0) - continue; - - if(decoded_frame->repeat_pict > 0) - CASPAR_LOG(warning) << "[video_decoder]: Field repeat_pict not implemented."; - - is_progressive_ = decoded_frame->interlaced_frame == 0; - - send(target_, make_message(decoded_frame, message->token)); - Concurrency::wait(10); - } - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - - send(is_running_, false), - send(target_, make_message(eof_video())); - - done(); - } - - double fps() const - { - return fps_; - } -}; - -video_decoder::video_decoder(video_decoder::source_t& source, video_decoder::target_t& target, AVFormatContext& context) - : impl_(new implementation(source, target, context)) -{ -} - -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_;} -bool video_decoder::is_progressive() const{return impl_->is_progressive_;} - -}} \ No newline at end of file +/* +* Copyright (c) 2011 Sveriges Television AB +* +* This file is part of CasparCG (www.casparcg.com). +* +* 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 . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ + +#include "../../StdAfx.h" + +#include "video_decoder.h" + +#include "../util/util.h" +#include "../input/input.h" + +#include "../../ffmpeg_error.h" + +#include +#include +#include + +#include + +#include + +#include + +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable : 4244) +#endif +extern "C" +{ + #include + #include +} +#if defined(_MSC_VER) +#pragma warning (pop) +#endif + +namespace caspar { namespace ffmpeg { + +struct video_decoder::impl : boost::noncopyable +{ + core::monitor::subject monitor_subject_; + input* input_; + int index_ = -1; + const spl::shared_ptr codec_context_; + + std::queue> packets_; + + const AVStream* stream_; + const uint32_t nb_frames_; + const int width_; + const int height_; + + tbb::atomic is_progressive_; + tbb::atomic file_frame_number_; + boost::rational framerate_; + + std::shared_ptr current_packet_; + +public: + explicit impl(input& in, bool single_threaded) + : input_(&in) + , codec_context_(open_codec(input_->context(), AVMEDIA_TYPE_VIDEO, index_, single_threaded)) + , stream_(input_->context().streams[index_]) + , nb_frames_(static_cast(stream_->nb_frames)) + , width_(codec_context_->width) + , height_(codec_context_->height) + , framerate_(read_framerate(input_->context(), 0)) + { + is_progressive_ = false; + file_frame_number_ = 0; + } + + std::shared_ptr poll() + { + if(!current_packet_ && !input_->try_pop_video(current_packet_)) + return nullptr; + + std::shared_ptr frame; + + if (!current_packet_->data) + { + if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) + frame = decode(*current_packet_); + + if (!frame) + { + file_frame_number_ = static_cast(current_packet_->pos); + avcodec_flush_buffers(codec_context_.get()); + current_packet_.reset(); + frame = flush(); + } + } + else + { + frame = decode(*current_packet_); + + if(current_packet_->size == 0) + current_packet_.reset(); + } + + return frame; + } + + std::shared_ptr decode(AVPacket& pkt) + { + auto frame = create_frame(); + + int got_frame = 0; + auto len = THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), frame.get(), &got_frame, &pkt), "[video_decocer]"); + + if(len == 0) + { + pkt.size = 0; + return nullptr; + } + + pkt.data += len; + pkt.size -= len; + + if(got_frame == 0) + return nullptr; + + ++file_frame_number_; + + is_progressive_ = !frame->interlaced_frame; + + if(frame->repeat_pict > 0) + CASPAR_LOG(warning) << "[video_decoder] repeat_pict not implemented."; + + monitor_subject_ << core::monitor::message("/file/video/width") % width_ + << core::monitor::message("/file/video/height") % height_ + << core::monitor::message("/file/video/field") % u8(!frame->interlaced_frame ? "progressive" : (frame->top_field_first ? "upper" : "lower")) + << core::monitor::message("/file/video/codec") % u8(codec_context_->codec->long_name); + + return frame; + } + + uint32_t nb_frames() const + { + return std::max(nb_frames_, static_cast(file_frame_number_)); + } + + std::wstring print() const + { + return L"[video-decoder] " + u16(codec_context_->codec->long_name); + } +}; + +video_decoder::video_decoder(input& in, bool single_threaded) : impl_(new impl(in, single_threaded)){} +video_decoder::video_decoder(video_decoder&& other) : impl_(std::move(other.impl_)){} +video_decoder& video_decoder::operator=(video_decoder&& other){impl_ = std::move(other.impl_); return *this;} +std::shared_ptr video_decoder::operator()(){return impl_->poll();} +int video_decoder::width() const{return impl_->width_;} +int video_decoder::height() const{return impl_->height_;} +uint32_t video_decoder::nb_frames() const{return impl_->nb_frames();} +uint32_t video_decoder::file_frame_number() const{return impl_->file_frame_number_;} +boost::rational video_decoder::framerate() const { return impl_->framerate_; } +bool video_decoder::is_progressive() const{return impl_->is_progressive_;} +std::wstring video_decoder::print() const{return impl_->print();} +core::monitor::subject& video_decoder::monitor_output() { return impl_->monitor_subject_; } +}}