mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00

Calculate the RMS audio level of audio packets being sent before invoking an encoded frame transform, and pass them with the encode frame object. Before this, the audio level was calculated at send time by having rms_levels_ look at all audio samples encoded since the last send. This is fine without a transform, as this is done synchronously after encoding, but with an async transform which might take arbitrarily long, we could end up marking older audio packets with newer audio levels, or not at all. This also makes things work correctly if external encoded frames are injected from elsewhere to be sent, and exposes the AudioLevel on the TransformableFrame interface. Bug: chromium:337193823, webrtc:42226202 Change-Id: If55d2c1d30dc03408ca9fb0193d791db44428316 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/349263 Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Tony Herre <herre@google.com> Cr-Commit-Position: refs/heads/main@{#42193}
179 lines
6.4 KiB
C++
179 lines
6.4 KiB
C++
/*
|
|
* Copyright (c) 2020 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 "audio/channel_receive_frame_transformer_delegate.h"
|
|
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "rtc_base/buffer.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
class TransformableIncomingAudioFrame
|
|
: public TransformableAudioFrameInterface {
|
|
public:
|
|
TransformableIncomingAudioFrame(rtc::ArrayView<const uint8_t> payload,
|
|
const RTPHeader& header,
|
|
uint32_t ssrc,
|
|
const std::string& codec_mime_type)
|
|
: payload_(payload.data(), payload.size()),
|
|
header_(header),
|
|
ssrc_(ssrc),
|
|
codec_mime_type_(codec_mime_type) {}
|
|
~TransformableIncomingAudioFrame() override = default;
|
|
rtc::ArrayView<const uint8_t> GetData() const override { return payload_; }
|
|
|
|
void SetData(rtc::ArrayView<const uint8_t> data) override {
|
|
payload_.SetData(data.data(), data.size());
|
|
}
|
|
|
|
void SetRTPTimestamp(uint32_t timestamp) override {
|
|
header_.timestamp = timestamp;
|
|
}
|
|
|
|
uint8_t GetPayloadType() const override { return header_.payloadType; }
|
|
uint32_t GetSsrc() const override { return ssrc_; }
|
|
uint32_t GetTimestamp() const override { return header_.timestamp; }
|
|
rtc::ArrayView<const uint32_t> GetContributingSources() const override {
|
|
return rtc::ArrayView<const uint32_t>(header_.arrOfCSRCs, header_.numCSRCs);
|
|
}
|
|
Direction GetDirection() const override { return Direction::kReceiver; }
|
|
|
|
std::string GetMimeType() const override { return codec_mime_type_; }
|
|
const absl::optional<uint16_t> SequenceNumber() const override {
|
|
return header_.sequenceNumber;
|
|
}
|
|
|
|
absl::optional<uint64_t> AbsoluteCaptureTimestamp() const override {
|
|
// This could be extracted from received header extensions + extrapolation,
|
|
// if required in future, eg for being able to re-send received frames.
|
|
return absl::nullopt;
|
|
}
|
|
const RTPHeader& Header() const { return header_; }
|
|
|
|
FrameType Type() const override {
|
|
if (!header_.extension.audio_level()) {
|
|
// Audio level extension not set.
|
|
return FrameType::kAudioFrameCN;
|
|
}
|
|
return header_.extension.audio_level()->voice_activity()
|
|
? FrameType::kAudioFrameSpeech
|
|
: FrameType::kAudioFrameCN;
|
|
}
|
|
|
|
absl::optional<uint8_t> AudioLevel() const override {
|
|
if (header_.extension.audio_level()) {
|
|
return header_.extension.audio_level()->level();
|
|
}
|
|
return absl::nullopt;
|
|
}
|
|
|
|
private:
|
|
rtc::Buffer payload_;
|
|
RTPHeader header_;
|
|
uint32_t ssrc_;
|
|
std::string codec_mime_type_;
|
|
};
|
|
} // namespace
|
|
|
|
ChannelReceiveFrameTransformerDelegate::ChannelReceiveFrameTransformerDelegate(
|
|
ReceiveFrameCallback receive_frame_callback,
|
|
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
|
|
TaskQueueBase* channel_receive_thread)
|
|
: receive_frame_callback_(receive_frame_callback),
|
|
frame_transformer_(std::move(frame_transformer)),
|
|
channel_receive_thread_(channel_receive_thread) {}
|
|
|
|
void ChannelReceiveFrameTransformerDelegate::Init() {
|
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
frame_transformer_->RegisterTransformedFrameCallback(
|
|
rtc::scoped_refptr<TransformedFrameCallback>(this));
|
|
}
|
|
|
|
void ChannelReceiveFrameTransformerDelegate::Reset() {
|
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
frame_transformer_->UnregisterTransformedFrameCallback();
|
|
frame_transformer_ = nullptr;
|
|
receive_frame_callback_ = ReceiveFrameCallback();
|
|
}
|
|
|
|
void ChannelReceiveFrameTransformerDelegate::Transform(
|
|
rtc::ArrayView<const uint8_t> packet,
|
|
const RTPHeader& header,
|
|
uint32_t ssrc,
|
|
const std::string& codec_mime_type) {
|
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
if (short_circuit_) {
|
|
receive_frame_callback_(packet, header);
|
|
} else {
|
|
frame_transformer_->Transform(
|
|
std::make_unique<TransformableIncomingAudioFrame>(packet, header, ssrc,
|
|
codec_mime_type));
|
|
}
|
|
}
|
|
|
|
void ChannelReceiveFrameTransformerDelegate::OnTransformedFrame(
|
|
std::unique_ptr<TransformableFrameInterface> frame) {
|
|
rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate(this);
|
|
channel_receive_thread_->PostTask(
|
|
[delegate = std::move(delegate), frame = std::move(frame)]() mutable {
|
|
delegate->ReceiveFrame(std::move(frame));
|
|
});
|
|
}
|
|
|
|
void ChannelReceiveFrameTransformerDelegate::StartShortCircuiting() {
|
|
rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate(this);
|
|
channel_receive_thread_->PostTask([delegate = std::move(delegate)]() mutable {
|
|
RTC_DCHECK_RUN_ON(&delegate->sequence_checker_);
|
|
delegate->short_circuit_ = true;
|
|
});
|
|
}
|
|
|
|
void ChannelReceiveFrameTransformerDelegate::ReceiveFrame(
|
|
std::unique_ptr<TransformableFrameInterface> frame) const {
|
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
if (!receive_frame_callback_)
|
|
return;
|
|
|
|
RTPHeader header;
|
|
if (frame->GetDirection() ==
|
|
TransformableFrameInterface::Direction::kSender) {
|
|
auto* transformed_frame =
|
|
static_cast<TransformableAudioFrameInterface*>(frame.get());
|
|
header.payloadType = transformed_frame->GetPayloadType();
|
|
header.timestamp = transformed_frame->GetTimestamp();
|
|
header.ssrc = transformed_frame->GetSsrc();
|
|
if (transformed_frame->AbsoluteCaptureTimestamp().has_value()) {
|
|
header.extension.absolute_capture_time = AbsoluteCaptureTime();
|
|
header.extension.absolute_capture_time->absolute_capture_timestamp =
|
|
transformed_frame->AbsoluteCaptureTimestamp().value();
|
|
}
|
|
} else {
|
|
auto* transformed_frame =
|
|
static_cast<TransformableIncomingAudioFrame*>(frame.get());
|
|
header = transformed_frame->Header();
|
|
}
|
|
|
|
// TODO(crbug.com/1464860): Take an explicit struct with the required
|
|
// information rather than the RTPHeader to make it easier to
|
|
// construct the required information when injecting transformed frames not
|
|
// originally from this receiver.
|
|
receive_frame_callback_(frame->GetData(), header);
|
|
}
|
|
|
|
rtc::scoped_refptr<FrameTransformerInterface>
|
|
ChannelReceiveFrameTransformerDelegate::FrameTransformer() {
|
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
return frame_transformer_;
|
|
}
|
|
|
|
} // namespace webrtc
|