| /* | |
| * Copyright (c) 2018 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 "api/audio/audio_frame.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <optional> | |
| #include <span> | |
| #include "absl/algorithm/container.h" | |
| #include "api/audio/audio_view.h" | |
| #include "api/audio/channel_layout.h" | |
| #include "api/rtp_packet_infos.h" | |
| #include "rtc_base/checks.h" | |
| namespace webrtc { | |
| AudioFrame::AudioFrame() { | |
| // Visual Studio doesn't like this in the class definition. | |
| static_assert(sizeof(data_) == kMaxDataSizeBytes, "kMaxDataSizeBytes"); | |
| } | |
| AudioFrame::AudioFrame(int sample_rate_hz, | |
| size_t num_channels, | |
| ChannelLayout layout /*= CHANNEL_LAYOUT_UNSUPPORTED*/) | |
| : samples_per_channel_(SampleRateToDefaultChannelSize(sample_rate_hz)), | |
| sample_rate_hz_(sample_rate_hz), | |
| num_channels_(num_channels), | |
| channel_layout_(layout == CHANNEL_LAYOUT_UNSUPPORTED | |
| ? GuessChannelLayout(num_channels) | |
| : layout) { | |
| RTC_DCHECK_LE(num_channels_, kMaxNumberOfAudioChannels); | |
| RTC_DCHECK_GT(sample_rate_hz_, 0); | |
| RTC_DCHECK_GT(samples_per_channel_, 0u); | |
| } | |
| void AudioFrame::Reset() { | |
| ResetWithoutMuting(); | |
| muted_ = true; | |
| } | |
| void AudioFrame::ResetWithoutMuting() { | |
| // TODO(wu): Zero is a valid value for `timestamp_`. We should initialize | |
| // to an invalid value, or add a new member to indicate invalidity. | |
| timestamp_ = 0; | |
| elapsed_time_ms_ = -1; | |
| ntp_time_ms_ = -1; | |
| samples_per_channel_ = 0; | |
| sample_rate_hz_ = 0; | |
| num_channels_ = 0; | |
| channel_layout_ = CHANNEL_LAYOUT_NONE; | |
| speech_type_ = kUndefined; | |
| vad_activity_ = kVadUnknown; | |
| packet_infos_ = RtpPacketInfos(); | |
| absolute_capture_timestamp_ms_ = std::nullopt; | |
| } | |
| void AudioFrame::UpdateFrame(uint32_t timestamp, | |
| const int16_t* data, | |
| size_t samples_per_channel, | |
| int sample_rate_hz, | |
| SpeechType speech_type, | |
| VADActivity vad_activity, | |
| size_t num_channels) { | |
| RTC_CHECK_LE(num_channels, kMaxNumberOfAudioChannels); | |
| timestamp_ = timestamp; | |
| samples_per_channel_ = samples_per_channel; | |
| sample_rate_hz_ = sample_rate_hz; | |
| speech_type_ = speech_type; | |
| vad_activity_ = vad_activity; | |
| num_channels_ = num_channels; | |
| channel_layout_ = GuessChannelLayout(num_channels); | |
| if (channel_layout_ != CHANNEL_LAYOUT_UNSUPPORTED) { | |
| RTC_DCHECK_EQ(num_channels, ChannelLayoutToChannelCount(channel_layout_)); | |
| } | |
| const size_t length = samples_per_channel * num_channels; | |
| RTC_CHECK_LE(length, data_.size()); | |
| if (data != nullptr) { | |
| memcpy(data_.data(), data, sizeof(int16_t) * length); | |
| muted_ = false; | |
| } else { | |
| muted_ = true; | |
| } | |
| } | |
| void AudioFrame::CopyFrom(const AudioFrame& src) { | |
| if (this == &src) | |
| return; | |
| if (muted_ && !src.muted()) { | |
| // TODO: bugs.webrtc.org/5647 - Since the default value for `muted_` is | |
| // false and `data_` may still be uninitialized (because we don't initialize | |
| // data_ as part of construction), we clear the full buffer here before | |
| // copying over new values. If we don't, msan might complain in some tests. | |
| // Consider locking down construction, avoiding the default constructor and | |
| // prefering construction that initializes all state. | |
| absl::c_fill(data_, 0); | |
| } | |
| timestamp_ = src.timestamp_; | |
| elapsed_time_ms_ = src.elapsed_time_ms_; | |
| ntp_time_ms_ = src.ntp_time_ms_; | |
| packet_infos_ = src.packet_infos_; | |
| muted_ = src.muted(); | |
| samples_per_channel_ = src.samples_per_channel_; | |
| sample_rate_hz_ = src.sample_rate_hz_; | |
| speech_type_ = src.speech_type_; | |
| vad_activity_ = src.vad_activity_; | |
| num_channels_ = src.num_channels_; | |
| channel_layout_ = src.channel_layout_; | |
| absolute_capture_timestamp_ms_ = src.absolute_capture_timestamp_ms(); | |
| auto data = src.data_view(); | |
| RTC_CHECK_LE(data.size(), data_.size()); | |
| if (!muted_ && !data.empty()) { | |
| memcpy(&data_[0], &data[0], sizeof(int16_t) * data.size()); | |
| } | |
| } | |
| const int16_t* AudioFrame::data() const { | |
| return muted_ ? zeroed_data().data() : data_.data(); | |
| } | |
| InterleavedView<const int16_t> AudioFrame::data_view() const { | |
| // If you get a nullptr from `data_view()`, it's likely because the | |
| // samples_per_channel_ and/or num_channels_ members haven't been properly | |
| // set. `data_view()` returns an InterleavedView<> which internally | |
| // uses std::span<>. So, even when an AudioFrame is muted and we want to | |
| // return `zeroed_data()`, if samples_per_channel_ or num_channels_ is 0, | |
| // the view might point to nullptr. | |
| return InterleavedView<const int16_t>(muted_ ? &zeroed_data()[0] : &data_[0], | |
| samples_per_channel_, num_channels_); | |
| } | |
| int16_t* AudioFrame::mutable_data() { | |
| // TODO: bugs.webrtc.org/5647 - Can we skip zeroing the buffer? | |
| // Consider instead if we should rather zero the buffer when `muted_` is set | |
| // to `true`. | |
| if (muted_) { | |
| absl::c_fill(data_, 0); | |
| muted_ = false; | |
| } | |
| return &data_[0]; | |
| } | |
| InterleavedView<int16_t> AudioFrame::mutable_data(size_t samples_per_channel, | |
| size_t num_channels) { | |
| const size_t total_samples = samples_per_channel * num_channels; | |
| RTC_CHECK_LE(total_samples, data_.size()); | |
| RTC_CHECK_LE(num_channels, kMaxNumberOfAudioChannels); | |
| // Sanity check for valid argument values during development. | |
| // If `samples_per_channel` is < `num_channels` but larger than 0, | |
| // then chances are the order of arguments is incorrect. | |
| RTC_DCHECK((samples_per_channel == 0 && num_channels == 0) || | |
| num_channels <= samples_per_channel) | |
| << "samples_per_channel=" << samples_per_channel | |
| << "num_channels=" << num_channels; | |
| // TODO: bugs.webrtc.org/5647 - Can we skip zeroing the buffer? | |
| // Consider instead if we should rather zero the whole buffer when `muted_` is | |
| // set to `true`. | |
| if (muted_) { | |
| absl::c_fill_n(data_, total_samples, 0); | |
| muted_ = false; | |
| } | |
| samples_per_channel_ = samples_per_channel; | |
| num_channels_ = num_channels; | |
| return InterleavedView<int16_t>(&data_[0], samples_per_channel, num_channels); | |
| } | |
| void AudioFrame::Mute() { | |
| muted_ = true; | |
| } | |
| bool AudioFrame::muted() const { | |
| return muted_; | |
| } | |
| void AudioFrame::SetLayoutAndNumChannels(ChannelLayout layout, | |
| size_t num_channels) { | |
| channel_layout_ = layout; | |
| num_channels_ = num_channels; | |
| #if RTC_DCHECK_IS_ON | |
| // Do a sanity check that the layout and num_channels match. | |
| // If this lookup yield 0u, then the layout is likely CHANNEL_LAYOUT_DISCRETE. | |
| auto expected_num_channels = ChannelLayoutToChannelCount(layout); | |
| if (expected_num_channels) { // If expected_num_channels is 0 | |
| RTC_DCHECK_EQ(expected_num_channels, num_channels_); | |
| } | |
| #endif | |
| RTC_CHECK_LE(samples_per_channel_ * num_channels_, data_.size()); | |
| } | |
| void AudioFrame::SetSampleRateAndChannelSize(int sample_rate) { | |
| sample_rate_hz_ = sample_rate; | |
| // We could call `AudioProcessing::GetFrameSize()` here, but that requires | |
| // adding a dependency on the ":audio_processing" build target, which can | |
| // complicate the dependency tree. Some refactoring is probably in order to | |
| // get some consistency around this since there are many places across the | |
| // code that assume this default buffer size. | |
| samples_per_channel_ = SampleRateToDefaultChannelSize(sample_rate_hz_); | |
| } | |
| // static | |
| std::span<const int16_t> AudioFrame::zeroed_data() { | |
| static int16_t* null_data = new int16_t[kMaxDataSizeSamples](); | |
| return std::span<const int16_t>(null_data, kMaxDataSizeSamples); | |
| } | |
| } // namespace webrtc |