X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Faudio%2Faudio_decoder.cpp;h=0dbbe4aa2c6aee32cbc32bd61a7d69e6877cdfd9;hb=726897adbf881d3b75f171fff24f2b917ba5f05a;hp=74ce106f772c4a35608c56b95cc3ecf2d7fa42d8;hpb=255d58b1bc4ae1067c9c1aea82343894b963bb95;p=casparcg diff --git a/modules/ffmpeg/producer/audio/audio_decoder.cpp b/modules/ffmpeg/producer/audio/audio_decoder.cpp index 74ce106f7..cd5b37792 100644 --- a/modules/ffmpeg/producer/audio/audio_decoder.cpp +++ b/modules/ffmpeg/producer/audio/audio_decoder.cpp @@ -1,71 +1,181 @@ -#include "../../stdafx.h" - -#include "audio_decoder.h" - -#if defined(_MSC_VER) -#pragma warning (push) -#pragma warning (disable : 4244) -#endif -extern "C" -{ - #define __STDC_CONSTANT_MACROS - #define __STDC_LIMIT_MACROS - #include - #include -} -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - -namespace caspar { - -struct audio_decoder::implementation : boost::noncopyable -{ - typedef std::vector> buffer; - - AVCodecContext* codec_context_; - - buffer audio_buffer_; - buffer current_chunk_; - - const size_t audio_frame_size_; - - static const size_t SAMPLE_RATE = 48000; - static const size_t N_CHANNELS = 2; - -public: - explicit implementation(AVCodecContext* codec_context, double fps) - : codec_context_(codec_context) - , audio_buffer_(4*SAMPLE_RATE*2+FF_INPUT_BUFFER_PADDING_SIZE/2) - , audio_frame_size_(static_cast(static_cast(SAMPLE_RATE) / fps) * N_CHANNELS) - { - if(!codec_context) - BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("codec_context")); - } - - std::vector> execute(const aligned_buffer& audio_packet) - { - int written_bytes = audio_buffer_.size()*2 - FF_INPUT_BUFFER_PADDING_SIZE; - const int result = avcodec_decode_audio2(codec_context_, audio_buffer_.data(), &written_bytes, audio_packet.data(), audio_packet.size()); - - if(result <= 0 || codec_context_->sample_rate != SAMPLE_RATE || codec_context_->channels != 2) - BOOST_THROW_EXCEPTION(invalid_operation() << msg_info("Invalid audio stream")); - - current_chunk_.insert(current_chunk_.end(), audio_buffer_.data(), audio_buffer_.data() + written_bytes/2); - - std::vector> chunks_; - - const auto last = current_chunk_.end() - current_chunk_.size() % audio_frame_size_; - - for(auto it = current_chunk_.begin(); it != last; it += audio_frame_size_) - chunks_.push_back(std::vector(it, it + audio_frame_size_)); - - current_chunk_.erase(current_chunk_.begin(), last); - - return chunks_; - } -}; - -audio_decoder::audio_decoder(AVCodecContext* codec_context, double fps) : impl_(new implementation(codec_context, fps)){} -std::vector> audio_decoder::execute(const aligned_buffer& audio_packet){return impl_->execute(audio_packet);} -} \ 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_ = -1; + 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(const spl::shared_ptr& context, int out_samplerate) + : 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_) + BOOST_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); + } +}; + +audio_decoder::audio_decoder(const spl::shared_ptr& context, int out_samplerate) : impl_(new implementation(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_->codec_context_->channel_layout; } +std::wstring audio_decoder::print() const{return impl_->print();} + +}}