| /* |
| * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/memory/memory.h" |
| #include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h" |
| #include "rtc_base/string_to_number.h" |
| |
| namespace webrtc { |
| |
| std::unique_ptr<AudioDecoderMultiChannelOpusImpl> |
| AudioDecoderMultiChannelOpusImpl::MakeAudioDecoder( |
| AudioDecoderMultiChannelOpusConfig config) { |
| if (!config.IsOk()) { |
| return nullptr; |
| } |
| // Fill the pointer with a working decoder through the C interface. This |
| // allocates memory. |
| OpusDecInst* dec_state = nullptr; |
| const int error = WebRtcOpus_MultistreamDecoderCreate( |
| &dec_state, config.num_channels, config.num_streams, |
| config.coupled_streams, config.channel_mapping.data()); |
| if (error != 0) { |
| return nullptr; |
| } |
| |
| // Pass the ownership to DecoderImpl. Not using 'make_unique' because the |
| // c-tor is private. |
| return std::unique_ptr<AudioDecoderMultiChannelOpusImpl>( |
| new AudioDecoderMultiChannelOpusImpl(dec_state, config)); |
| } |
| |
| AudioDecoderMultiChannelOpusImpl::AudioDecoderMultiChannelOpusImpl( |
| OpusDecInst* dec_state, |
| AudioDecoderMultiChannelOpusConfig config) |
| : dec_state_(dec_state), config_(config) { |
| RTC_DCHECK(dec_state); |
| WebRtcOpus_DecoderInit(dec_state_); |
| } |
| |
| AudioDecoderMultiChannelOpusImpl::~AudioDecoderMultiChannelOpusImpl() { |
| WebRtcOpus_DecoderFree(dec_state_); |
| } |
| |
| absl::optional<AudioDecoderMultiChannelOpusConfig> |
| AudioDecoderMultiChannelOpusImpl::SdpToConfig(const SdpAudioFormat& format) { |
| AudioDecoderMultiChannelOpusConfig config; |
| config.num_channels = format.num_channels; |
| auto num_streams = GetFormatParameter<int>(format, "num_streams"); |
| if (!num_streams.has_value()) { |
| return absl::nullopt; |
| } |
| config.num_streams = *num_streams; |
| |
| auto coupled_streams = GetFormatParameter<int>(format, "coupled_streams"); |
| if (!coupled_streams.has_value()) { |
| return absl::nullopt; |
| } |
| config.coupled_streams = *coupled_streams; |
| |
| auto channel_mapping = |
| GetFormatParameter<std::vector<unsigned char>>(format, "channel_mapping"); |
| if (!channel_mapping.has_value()) { |
| return absl::nullopt; |
| } |
| config.channel_mapping = *channel_mapping; |
| return config; |
| } |
| |
| std::vector<AudioDecoder::ParseResult> |
| AudioDecoderMultiChannelOpusImpl::ParsePayload(rtc::Buffer&& payload, |
| uint32_t timestamp) { |
| std::vector<ParseResult> results; |
| |
| if (PacketHasFec(payload.data(), payload.size())) { |
| const int duration = |
| PacketDurationRedundant(payload.data(), payload.size()); |
| RTC_DCHECK_GE(duration, 0); |
| rtc::Buffer payload_copy(payload.data(), payload.size()); |
| std::unique_ptr<EncodedAudioFrame> fec_frame( |
| new OpusFrame(this, std::move(payload_copy), false)); |
| results.emplace_back(timestamp - duration, 1, std::move(fec_frame)); |
| } |
| std::unique_ptr<EncodedAudioFrame> frame( |
| new OpusFrame(this, std::move(payload), true)); |
| results.emplace_back(timestamp, 0, std::move(frame)); |
| return results; |
| } |
| |
| int AudioDecoderMultiChannelOpusImpl::DecodeInternal(const uint8_t* encoded, |
| size_t encoded_len, |
| int sample_rate_hz, |
| int16_t* decoded, |
| SpeechType* speech_type) { |
| RTC_DCHECK_EQ(sample_rate_hz, 48000); |
| int16_t temp_type = 1; // Default is speech. |
| int ret = |
| WebRtcOpus_Decode(dec_state_, encoded, encoded_len, decoded, &temp_type); |
| if (ret > 0) |
| ret *= static_cast<int>( |
| config_.num_channels); // Return total number of samples. |
| *speech_type = ConvertSpeechType(temp_type); |
| return ret; |
| } |
| |
| int AudioDecoderMultiChannelOpusImpl::DecodeRedundantInternal( |
| const uint8_t* encoded, |
| size_t encoded_len, |
| int sample_rate_hz, |
| int16_t* decoded, |
| SpeechType* speech_type) { |
| if (!PacketHasFec(encoded, encoded_len)) { |
| // This packet is a RED packet. |
| return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded, |
| speech_type); |
| } |
| |
| RTC_DCHECK_EQ(sample_rate_hz, 48000); |
| int16_t temp_type = 1; // Default is speech. |
| int ret = WebRtcOpus_DecodeFec(dec_state_, encoded, encoded_len, decoded, |
| &temp_type); |
| if (ret > 0) |
| ret *= static_cast<int>( |
| config_.num_channels); // Return total number of samples. |
| *speech_type = ConvertSpeechType(temp_type); |
| return ret; |
| } |
| |
| void AudioDecoderMultiChannelOpusImpl::Reset() { |
| WebRtcOpus_DecoderInit(dec_state_); |
| } |
| |
| int AudioDecoderMultiChannelOpusImpl::PacketDuration(const uint8_t* encoded, |
| size_t encoded_len) const { |
| return WebRtcOpus_DurationEst(dec_state_, encoded, encoded_len); |
| } |
| |
| int AudioDecoderMultiChannelOpusImpl::PacketDurationRedundant( |
| const uint8_t* encoded, |
| size_t encoded_len) const { |
| if (!PacketHasFec(encoded, encoded_len)) { |
| // This packet is a RED packet. |
| return PacketDuration(encoded, encoded_len); |
| } |
| |
| return WebRtcOpus_FecDurationEst(encoded, encoded_len, 48000); |
| } |
| |
| bool AudioDecoderMultiChannelOpusImpl::PacketHasFec(const uint8_t* encoded, |
| size_t encoded_len) const { |
| int fec; |
| fec = WebRtcOpus_PacketHasFec(encoded, encoded_len); |
| return (fec == 1); |
| } |
| |
| int AudioDecoderMultiChannelOpusImpl::SampleRateHz() const { |
| return 48000; |
| } |
| |
| size_t AudioDecoderMultiChannelOpusImpl::Channels() const { |
| return config_.num_channels; |
| } |
| |
| } // namespace webrtc |