X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Faudio%2Faudio_decoder.cpp;h=2366c10d8032260be15a9ca11197455926c528ce;hb=92385caba7ef84a392dcbafd6dfb0c606b42e519;hp=a8461143eb08c5d2fad8c9571c55ccbd7c03f982;hpb=c5f0ad65444bd8f55d9cb5f04f726d9e6f36c964;p=casparcg diff --git a/modules/ffmpeg/producer/audio/audio_decoder.cpp b/modules/ffmpeg/producer/audio/audio_decoder.cpp index a8461143e..2366c10d8 100644 --- a/modules/ffmpeg/producer/audio/audio_decoder.cpp +++ b/modules/ffmpeg/producer/audio/audio_decoder.cpp @@ -1,162 +1,192 @@ -/* -* 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 "audio_decoder.h" -#include "audio_resampler.h" - -#include "../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 -} -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - -#undef Yield -using namespace Concurrency; - -namespace caspar { namespace ffmpeg { - -struct audio_decoder::implementation : public agent, boost::noncopyable -{ - int index_; - std::shared_ptr codec_context_; - - audio_resampler resampler_; - - std::vector> buffer1_; - - unbounded_buffer source_; - ITarget& target_; - - governor governor_; - tbb::atomic is_running_; - -public: - explicit implementation(audio_decoder::source_t& source, audio_decoder::target_t& target, AVFormatContext& context, const core::video_format_desc& format_desc) - : codec_context_(open_codec(context, AVMEDIA_TYPE_AUDIO, index_)) - , resampler_(format_desc.audio_channels, codec_context_->channels, - format_desc.audio_sample_rate, codec_context_->sample_rate, - AV_SAMPLE_FMT_S32, codec_context_->sample_fmt) - , buffer1_(AVCODEC_MAX_AUDIO_FRAME_SIZE*2) - , source_([this](const audio_decoder::source_element_t& packet){return packet->stream_index == index_;}) - , target_(target) - , governor_(2) - { - CASPAR_LOG(debug) << "[audio_decoder] " << context.streams[index_]->codec->codec->long_name; - - source.link_target(&source_); - - is_running_ = true; - start(); - } - - ~implementation() - { - is_running_ = false; - governor_.cancel(); - agent::wait(this); - } - - virtual void run() - { - win32_exception::install_handler(); - - try - { - while(is_running_) - { - auto ticket = governor_.acquire(); - auto packet = receive(source_); - - if(packet == loop_packet(index_)) - { - avcodec_flush_buffers(codec_context_.get()); - send(target_, loop_audio()); - continue; - } - - if(packet == eof_packet(index_)) - break; - - auto result = std::make_shared(); - - while(packet->size > 0) - { - buffer1_.resize(AVCODEC_MAX_AUDIO_FRAME_SIZE*2); - int written_bytes = buffer1_.size() - FF_INPUT_BUFFER_PADDING_SIZE; - - int ret = THROW_ON_ERROR2(avcodec_decode_audio3(codec_context_.get(), reinterpret_cast(buffer1_.data()), &written_bytes, packet.get()), "[audio_decoder]"); - - // There might be several frames in one packet. - packet->size -= ret; - packet->data += ret; - - buffer1_.resize(written_bytes); - - buffer1_ = resampler_.resample(std::move(buffer1_)); - - const auto n_samples = buffer1_.size() / av_get_bytes_per_sample(AV_SAMPLE_FMT_S32); - const auto samples = reinterpret_cast(buffer1_.data()); - - auto audio = make_safe(samples, samples + n_samples); - - send(target_, safe_ptr(audio.get(), [audio, ticket](core::audio_buffer*){})); - Context::Yield(); - } - } - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - } - - send(target_, eof_audio()); - - done(); - } -}; - -audio_decoder::audio_decoder(audio_decoder::source_t& source, audio_decoder::target_t& target, AVFormatContext& context, const core::video_format_desc& format_desc) - : impl_(new implementation(source, target, context, format_desc)) -{ -} - -int64_t audio_decoder::nb_frames() const -{ - return 0; -} - -}} \ 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 "audio_decoder.h" + +#include "../util/util.h" +#include "../input/input.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 + #include +} +#if defined(_MSC_VER) +#pragma warning (pop) +#endif + +namespace caspar { namespace ffmpeg { + +uint64_t get_ffmpeg_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 +{ + core::monitor::subject monitor_subject_; + input& input_; + int index_; + int actual_index_; + const core::video_format_desc format_desc_; + const spl::shared_ptr codec_context_ = open_codec(input_.context(), AVMEDIA_TYPE_AUDIO, actual_index_, false); + + std::shared_ptr swr_ { + swr_alloc_set_opts( + nullptr, + create_channel_layout_bitmask(codec_context_->channels),//get_ffmpeg_channel_layout(codec_context_.get()), + AV_SAMPLE_FMT_S32, + format_desc_.audio_sample_rate, + create_channel_layout_bitmask(codec_context_->channels),//get_ffmpeg_channel_layout(codec_context_.get()), + codec_context_->sample_fmt, + codec_context_->sample_rate, + 0, + nullptr), + [](SwrContext* p){swr_free(&p); } + }; + + cache_aligned_vector buffer_; + + std::shared_ptr current_packet_; + +public: + explicit impl( + input& in, + const core::video_format_desc& format_desc, + int audio_stream_index) + : input_(in) + , index_(audio_stream_index) + , actual_index_(input_.get_actual_audio_stream_index(index_)) + , format_desc_(format_desc) + , buffer_(480000 * 4) + { + if(!swr_) + CASPAR_THROW_EXCEPTION(bad_alloc()); + + THROW_ON_ERROR2(swr_init(swr_.get()), "[audio_decoder]"); + } + + std::shared_ptr poll() + { + if(!current_packet_ && !input_.try_pop_audio(current_packet_, index_)) + 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; + } + + std::shared_ptr decode(AVPacket& pkt) + { + auto frame = create_frame(); + + int got_frame = 0; + auto len = THROW_ON_ERROR2(avcodec_decode_audio4(codec_context_.get(), 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(frame->extended_data); + uint8_t* out[] = {buffer_.data()}; + + auto channel_samples = swr_convert(swr_.get(), + out, static_cast(buffer_.size()) / codec_context_->channels / av_get_bytes_per_sample(AV_SAMPLE_FMT_S32), + in, frame->nb_samples); + + frame->data[0] = buffer_.data(); + frame->linesize[0] = channel_samples * codec_context_->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S32); + frame->nb_samples = channel_samples; + frame->format = AV_SAMPLE_FMT_S32; + + monitor_subject_ << core::monitor::message("/file/audio/sample-rate") % codec_context_->sample_rate + << core::monitor::message("/file/audio/channels") % codec_context_->channels + << core::monitor::message("/file/audio/format") % u8(av_get_sample_fmt_name(codec_context_->sample_fmt)) + << core::monitor::message("/file/audio/codec") % u8(codec_context_->codec->long_name); + + return frame; + } + + 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(input& input, const core::video_format_desc& format_desc, int audio_stream_index) : impl_(new impl(input, format_desc, audio_stream_index)){} +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();} +core::monitor::subject& audio_decoder::monitor_output() { return impl_->monitor_subject_;} + +}}