X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Fvideo%2Fvideo_decoder.cpp;h=cc21e88e4cfc10288d43c8453af7683461905527;hb=f6ef89b1598a3c9e97857611f802703768740b36;hp=429f78ff31c3c91ca94cb796fb856be0bc59f0e4;hpb=2ad082d0d6b09aa61b5782bec11ebab17794eaca;p=casparcg diff --git a/modules/ffmpeg/producer/video/video_decoder.cpp b/modules/ffmpeg/producer/video/video_decoder.cpp index 429f78ff3..cc21e88e4 100644 --- a/modules/ffmpeg/producer/video/video_decoder.cpp +++ b/modules/ffmpeg/producer/video/video_decoder.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2011 Sveriges Television AB +* Copyright 2013 Sveriges Television AB http://casparcg.com/ * * This file is part of CasparCG (www.casparcg.com). * @@ -24,14 +24,13 @@ #include "video_decoder.h" #include "../util/util.h" -#include "../input/input.h" #include "../../ffmpeg_error.h" -#include #include #include +#include #include #include @@ -51,110 +50,104 @@ extern "C" namespace caspar { namespace ffmpeg { -struct video_decoder::impl : boost::noncopyable +struct video_decoder::implementation : boost::noncopyable { - core::monitor::subject monitor_subject_; - input* input_; - int index_; + 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_; + const int width_ = codec_context_->width; + const int height_ = codec_context_->height; bool is_progressive_; - uint32_t file_frame_number_; - boost::rational framerate_; - - std::shared_ptr current_packet_; + + tbb::atomic file_frame_number_; 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) - , file_frame_number_(0) - , framerate_(read_framerate(input_->context(), 0)) + 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(!current_packet_ && !input_->try_pop_video(current_packet_)) + { + if(packets_.empty()) return nullptr; - std::shared_ptr frame; - - if(!current_packet_) - { - avcodec_flush_buffers(codec_context_.get()); + 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(); } - 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; + packets_.pop(); + return decode(packet); } - std::shared_ptr decode(AVPacket& pkt) + std::shared_ptr decode(spl::shared_ptr 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) + auto decoded_frame = std::shared_ptr(av_frame_alloc(), [](AVFrame* frame) { - pkt.size = 0; - return nullptr; - } - - pkt.data += len; - pkt.size -= len; + 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(got_frame == 0) + if(frame_finished == 0) return nullptr; - - auto stream_time_base = stream_->time_base; - auto fps = static_cast(framerate_.numerator()) / static_cast(framerate_.denominator()); - 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_ = !decoded_frame->interlaced_frame; - 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); + if(decoded_frame->repeat_pict > 0) + CASPAR_LOG(warning) << "[video_decoder] Field repeat_pict not implemented."; - return frame; + ++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_, file_frame_number_); + return std::max(nb_frames_, static_cast(file_frame_number_)); } std::wstring print() const @@ -163,16 +156,15 @@ public: } }; -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();} +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 impl_->file_frame_number_;} -boost::rational video_decoder::framerate() const { return impl_->framerate_; } -bool video_decoder::is_progressive() const{return impl_->is_progressive_;} +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();} -core::monitor::subject& video_decoder::monitor_output() { return impl_->monitor_subject_; } + }}