X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Faudio%2Faudio_decoder.cpp;h=0a1008876583afed71cbc24252315e4d5a3e6a48;hb=009816de6e071c6a35c74b0954d04cf61005b971;hp=3a341c923f24526d0a65ac10b77fa2da2b268be0;hpb=afaeaa084249be76894758ab662ff0da1689818f;p=casparcg diff --git a/modules/ffmpeg/producer/audio/audio_decoder.cpp b/modules/ffmpeg/producer/audio/audio_decoder.cpp index 3a341c923..0a1008876 100644 --- a/modules/ffmpeg/producer/audio/audio_decoder.cpp +++ b/modules/ffmpeg/producer/audio/audio_decoder.cpp @@ -1,186 +1,190 @@ -/* -* 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 "audio_decoder.h" - -#include "../util/util.h" -#include "../input/input.h" -#include "../../ffmpeg_error.h" - -#include - -#include - -#include - -#include - -#if defined(_MSC_VER) -#pragma warning (push) -#pragma warning (disable : 4244) -#endif -extern "C" -{ - #include - #include - #include -} -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - -namespace caspar { namespace ffmpeg { - -uint64_t get_channel_layout(AVCodecContext* dec) -{ - auto layout = (dec->channel_layout && dec->channels == av_get_channel_layout_nb_channels(dec->channel_layout)) ? dec->channel_layout : av_get_default_channel_layout(dec->channels); - return layout; -} - -struct audio_decoder::impl : boost::noncopyable -{ - monitor::basic_subject event_subject_; - input* input_; - int index_; - const std::shared_ptr codec_context_; - const core::video_format_desc format_desc_; - - std::shared_ptr swr_; - - std::vector> buffer_; - - std::shared_ptr current_packet_; - -public: - impl() - : input_(nullptr) - { - } - - explicit impl(input& in, const core::video_format_desc& format_desc) - : input_(&in) - , format_desc_(format_desc) - , codec_context_(open_codec(input_->context(), AVMEDIA_TYPE_AUDIO, index_)) - , swr_(swr_alloc_set_opts(nullptr, - av_get_default_channel_layout(format_desc_.audio_channels), AV_SAMPLE_FMT_S32, format_desc_.audio_sample_rate, - get_channel_layout(codec_context_.get()), codec_context_->sample_fmt, codec_context_->sample_rate, - 0, nullptr), [](SwrContext* p){swr_free(&p);}) - , buffer_(AVCODEC_MAX_AUDIO_FRAME_SIZE*4) - { - if(!swr_) - CASPAR_THROW_EXCEPTION(bad_alloc()); - - THROW_ON_ERROR2(swr_init(swr_.get()), "[audio_decoder]"); - } - - std::shared_ptr poll() - { - if(!codec_context_) - return empty_audio(); - - if(!current_packet_ && !input_->try_pop_audio(current_packet_)) - return nullptr; - - std::shared_ptr audio; - - if(!current_packet_) - { - avcodec_flush_buffers(codec_context_.get()); - } - else if(!current_packet_->data) - { - if(codec_context_->codec->capabilities & CODEC_CAP_DELAY) - audio = decode(*current_packet_); - - if(!audio) - current_packet_.reset(); - } - else - { - audio = decode(*current_packet_); - - if(current_packet_->size == 0) - current_packet_.reset(); - } - - return audio ? audio : poll(); - } - - std::shared_ptr decode(AVPacket& pkt) - { - std::shared_ptr decoded_frame(avcodec_alloc_frame(), av_free); - - int got_frame = 0; - auto len = THROW_ON_ERROR2(avcodec_decode_audio4(codec_context_.get(), decoded_frame.get(), &got_frame, &pkt), "[audio_decoder]"); - - if(len == 0) - { - pkt.size = 0; - return nullptr; - } - - pkt.data += len; - pkt.size -= len; - - if(!got_frame) - return nullptr; - - const uint8_t *in[] = {decoded_frame->data[0]}; - uint8_t* out[] = {buffer_.data()}; - - auto channel_samples = swr_convert(swr_.get(), - out, static_cast(buffer_.size()) / format_desc_.audio_channels / av_get_bytes_per_sample(AV_SAMPLE_FMT_S32), - in, decoded_frame->nb_samples); - - auto ptr = reinterpret_cast(buffer_.data()); - - event_subject_ << monitor::event("file/audio/sample-rate") % codec_context_->sample_rate - << monitor::event("file/audio/channels") % codec_context_->channels - << monitor::event("file/audio/format") % u8(av_get_sample_fmt_name(codec_context_->sample_fmt)) - << monitor::event("file/audio/codec") % u8(codec_context_->codec->long_name); - - return std::make_shared(ptr, ptr + channel_samples * format_desc_.audio_channels); - } - - uint32_t nb_frames() const - { - return 0; - } - - std::wstring print() const - { - return L"[audio-decoder] " + u16(codec_context_->codec->long_name); - } -}; - -audio_decoder::audio_decoder() : impl_(new impl()){} -audio_decoder::audio_decoder(input& input, const core::video_format_desc& format_desc) : impl_(new impl(input, format_desc)){} -audio_decoder::audio_decoder(audio_decoder&& other) : impl_(std::move(other.impl_)){} -audio_decoder& audio_decoder::operator=(audio_decoder&& other){impl_ = std::move(other.impl_); return *this;} -std::shared_ptr audio_decoder::operator()(){return impl_->poll();} -uint32_t audio_decoder::nb_frames() const{return impl_->nb_frames();} -std::wstring audio_decoder::print() const{return impl_->print();} -void audio_decoder::subscribe(const monitor::observable::observer_ptr& o){impl_->event_subject_.subscribe(o);} -void audio_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 "audio_decoder.h" + +#include "../util/util.h" +#include "../../ffmpeg_error.h" + +#include +#include + +#include + +#include + +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable : 4244) +#endif +extern "C" +{ + #include + #include + #include +} +#if defined(_MSC_VER) +#pragma warning (pop) +#endif + +namespace caspar { namespace ffmpeg { + +struct audio_decoder::implementation : boost::noncopyable +{ + int index_; + const spl::shared_ptr codec_context_; + const int out_samplerate_; + + cache_aligned_vector buffer_; + + std::queue> packets_; + + std::shared_ptr swr_ { + swr_alloc_set_opts( + nullptr, + codec_context_->channel_layout + ? codec_context_->channel_layout + : av_get_default_channel_layout(codec_context_->channels), + AV_SAMPLE_FMT_S32, + out_samplerate_, + codec_context_->channel_layout + ? codec_context_->channel_layout + : av_get_default_channel_layout(codec_context_->channels), + codec_context_->sample_fmt, + codec_context_->sample_rate, + 0, + nullptr), + [](SwrContext* p) + { + swr_free(&p); + } + }; + +public: + explicit implementation(int stream_index, const spl::shared_ptr& context, int out_samplerate) + : index_(stream_index) + , codec_context_(open_codec(*context, AVMEDIA_TYPE_AUDIO, index_, false)) + , out_samplerate_(out_samplerate) + , buffer_(10 * out_samplerate_ * codec_context_->channels) // 10 seconds of audio + { + if(!swr_) + CASPAR_THROW_EXCEPTION(bad_alloc()); + + THROW_ON_ERROR2(swr_init(swr_.get()), "[audio_decoder]"); + + 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) + { + packets_.pop(); + avcodec_flush_buffers(codec_context_.get()); + return flush_audio(); + } + + auto audio = decode(*packet); + + if(packet->size == 0) + packets_.pop(); + + return audio; + } + + std::shared_ptr decode(AVPacket& pkt) + { + auto decoded_frame = create_frame(); + + int got_frame = 0; + auto len = THROW_ON_ERROR2(avcodec_decode_audio4(codec_context_.get(), decoded_frame.get(), &got_frame, &pkt), "[audio_decoder]"); + + if (len == 0) + { + pkt.size = 0; + return nullptr; + } + + pkt.data += len; + pkt.size -= len; + + if (!got_frame) + return nullptr; + + const uint8_t **in = const_cast(decoded_frame->extended_data); + uint8_t* out[] = { reinterpret_cast(buffer_.data()) }; + + const auto channel_samples = swr_convert( + swr_.get(), + out, + static_cast(buffer_.size()) / codec_context_->channels, + in, + decoded_frame->nb_samples); + + return std::make_shared( + buffer_.begin(), + buffer_.begin() + channel_samples * decoded_frame->channels); + } + + bool ready() const + { + return packets_.size() > 10; + } + + std::wstring print() const + { + return L"[audio-decoder] " + u16(codec_context_->codec->long_name); + } + + uint64_t ffmpeg_channel_layout() const + { + if (codec_context_->channel_layout == 0) + return av_get_default_channel_layout(codec_context_->channels); + else + return codec_context_->channel_layout; + } +}; + +audio_decoder::audio_decoder(int stream_index, const spl::shared_ptr& context, int out_samplerate) : impl_(new implementation(stream_index, context, out_samplerate)){} +void audio_decoder::push(const std::shared_ptr& packet){impl_->push(packet);} +bool audio_decoder::ready() const{return impl_->ready();} +std::shared_ptr audio_decoder::poll() { return impl_->poll(); } +int audio_decoder::num_channels() const { return impl_->codec_context_->channels; } +uint64_t audio_decoder::ffmpeg_channel_layout() const { return impl_->ffmpeg_channel_layout(); } +std::wstring audio_decoder::print() const{return impl_->print();} + +}}