X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Fvideo%2Fvideo_decoder.cpp;h=cc21e88e4cfc10288d43c8453af7683461905527;hb=c0dc760a3d87b346c9f267cd9d74c67c55d3bdc3;hp=5fe8e477d9abd25d151e46e8b913806419bda294;hpb=afaeaa084249be76894758ab662ff0da1689818f;p=casparcg diff --git a/modules/ffmpeg/producer/video/video_decoder.cpp b/modules/ffmpeg/producer/video/video_decoder.cpp index 5fe8e477d..cc21e88e4 100644 --- a/modules/ffmpeg/producer/video/video_decoder.cpp +++ b/modules/ffmpeg/producer/video/video_decoder.cpp @@ -1,193 +1,170 @@ -/* -* 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 -{ - monitor::basic_subject event_subject_; - input* input_; - int index_; - const std::shared_ptr codec_context_; - - std::queue> packets_; - - const AVStream* stream_; - const uint32_t nb_frames_; - const int width_; - const int height_; - - bool is_progressive_; - uint32_t file_frame_number_; - double fps_; - - std::shared_ptr current_packet_; - -public: - explicit impl() - : input_(nullptr) - , nb_frames_(0) - , width_(0) - , height_(0) - , file_frame_number_(0) - , fps_(0.0) - { - } - - explicit impl(input& in) - : input_(&in) - , codec_context_(open_codec(input_->context(), AVMEDIA_TYPE_VIDEO, index_)) - , stream_(input_->context().streams[index_]) - , nb_frames_(static_cast(stream_->nb_frames)) - , width_(codec_context_->width) - , height_(codec_context_->height) - , file_frame_number_(0) - , fps_(read_fps(input_->context(), 0.0)) - { - } - - std::shared_ptr poll() - { - if(!codec_context_) - return empty_video(); - - if(!current_packet_ && !input_->try_pop_video(current_packet_)) - return nullptr; - - std::shared_ptr frame; - - if(!current_packet_) - { - avcodec_flush_buffers(codec_context_.get()); - } - else if(!current_packet_->data) - { - if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) - frame = decode(*current_packet_); - - if(!frame) - current_packet_.reset(); - } - else - { - frame = decode(*current_packet_); - - if(current_packet_->size == 0) - current_packet_.reset(); - } - - return frame ? frame : poll(); - } - - std::shared_ptr decode(AVPacket& pkt) - { - auto frame = std::shared_ptr(avcodec_alloc_frame(), av_free); - - 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; - - auto stream_time_base = stream_->time_base; - auto packet_frame_number = static_cast((static_cast(pkt.pts * stream_time_base.num)/stream_time_base.den)*fps_); - - file_frame_number_ = packet_frame_number; - - is_progressive_ = !frame->interlaced_frame; - - if(frame->repeat_pict > 0) - CASPAR_LOG(warning) << "[video_decoder] repeat_pict not implemented."; - - event_subject_ << monitor::event("file/video/width") % width_ - << monitor::event("file/video/height") % height_ - << monitor::event("file/video/field") % u8(!frame->interlaced_frame ? "progressive" : (frame->top_field_first ? "upper" : "lower")) - << monitor::event("file/video/codec") % u8(codec_context_->codec->long_name); - - return frame; - } - - uint32_t nb_frames() const - { - return std::max(nb_frames_, file_frame_number_); - } - - std::wstring print() const - { - return L"[video-decoder] " + u16(codec_context_->codec->long_name); - } -}; - -video_decoder::video_decoder() : impl_(new impl()){} -video_decoder::video_decoder(input& in) : impl_(new impl(in)){} -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_;} -bool video_decoder::is_progressive() const{return impl_->is_progressive_;} -std::wstring video_decoder::print() const{return impl_->print();} -void video_decoder::subscribe(const monitor::observable::observer_ptr& o){impl_->event_subject_.subscribe(o);} -void video_decoder::unsubscribe(const monitor::observable::observer_ptr& o){impl_->event_subject_.unsubscribe(o);} - -}} \ No newline at end of file +/* +* Copyright 2013 Sveriges Television AB http://casparcg.com/ +* +* 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 "../../ffmpeg_error.h" + +#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::implementation : boost::noncopyable +{ + int index_ = -1; + const spl::shared_ptr codec_context_; + + std::queue> packets_; + + const uint32_t nb_frames_; + + const int width_ = codec_context_->width; + const int height_ = codec_context_->height; + bool is_progressive_; + + tbb::atomic file_frame_number_; + +public: + explicit implementation(const spl::shared_ptr& context) + : codec_context_(open_codec(*context, AVMEDIA_TYPE_VIDEO, index_, false)) + , nb_frames_(static_cast(context->streams[index_]->nb_frames)) + { + file_frame_number_ = 0; + + codec_context_->refcounted_frames = 1; + } + + void push(const std::shared_ptr& packet) + { + if(!packet) + return; + + if(packet->stream_index == index_ || packet->data == nullptr) + packets_.push(spl::make_shared_ptr(packet)); + } + + std::shared_ptr poll() + { + if(packets_.empty()) + return nullptr; + + auto packet = packets_.front(); + + if(packet->data == nullptr) + { + if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) + { + auto video = decode(packet); + if(video) + return video; + } + + packets_.pop(); + file_frame_number_ = static_cast(packet->pos); + avcodec_flush_buffers(codec_context_.get()); + return flush_video(); + } + + packets_.pop(); + return decode(packet); + } + + std::shared_ptr decode(spl::shared_ptr pkt) + { + auto decoded_frame = std::shared_ptr(av_frame_alloc(), [](AVFrame* frame) + { + av_frame_free(&frame); + }); + + int frame_finished = 0; + THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, pkt.get()), "[video_decoder]"); + + // 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) + return nullptr; + + is_progressive_ = !decoded_frame->interlaced_frame; + + if(decoded_frame->repeat_pict > 0) + CASPAR_LOG(warning) << "[video_decoder] Field repeat_pict not implemented."; + + ++file_frame_number_; + + // This ties the life of the decoded_frame to the packet that it came from. For the + // current version of ffmpeg (0.8 or c17808c) the RAW_VIDEO codec returns frame data + // owned by the packet. + return std::shared_ptr(decoded_frame.get(), [decoded_frame, pkt](AVFrame*){}); + } + + bool ready() const + { + return packets_.size() >= 8; + } + + 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(const spl::shared_ptr& context) : impl_(new implementation(context)){} +void video_decoder::push(const std::shared_ptr& packet){impl_->push(packet);} +std::shared_ptr video_decoder::poll(){return impl_->poll();} +bool video_decoder::ready() const{return impl_->ready();} +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 static_cast(impl_->file_frame_number_);} +bool video_decoder::is_progressive() const{return impl_->is_progressive_;} +std::wstring video_decoder::print() const{return impl_->print();} + +}}