blob: 4cec7aaf7835c1882b82990b67d9be16ba1b12e0 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.h"
#include <utility>
#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_audio_frame_metadata.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/webrtc/api/frame_transformer_interface.h"
namespace blink {
namespace {
struct SetMetadataValidationOutcome {
bool allowed;
String error_msg;
};
SetMetadataValidationOutcome IsAllowedSetMetadataChange(
const RTCEncodedAudioFrameMetadata* current_metadata,
const RTCEncodedAudioFrameMetadata* new_metadata) {
// Only changing the RTP Timestamp is supported.
if (new_metadata->hasSynchronizationSource() !=
current_metadata->hasSynchronizationSource() ||
(new_metadata->hasSynchronizationSource() &&
current_metadata->synchronizationSource() !=
new_metadata->synchronizationSource())) {
return SetMetadataValidationOutcome{false, "Bad synchronizationSource"};
}
if (new_metadata->hasContributingSources() !=
current_metadata->hasContributingSources() ||
(new_metadata->hasContributingSources() &&
current_metadata->contributingSources() !=
new_metadata->contributingSources())) {
return SetMetadataValidationOutcome{false, "Bad contributingSources"};
}
if (new_metadata->hasPayloadType() != current_metadata->hasPayloadType() ||
(new_metadata->hasPayloadType() &&
current_metadata->payloadType() != new_metadata->payloadType())) {
return SetMetadataValidationOutcome{false, "Bad payloadType"};
}
if (new_metadata->hasSequenceNumber() !=
current_metadata->hasSequenceNumber() ||
(new_metadata->hasSequenceNumber() &&
current_metadata->sequenceNumber() != new_metadata->sequenceNumber())) {
return SetMetadataValidationOutcome{false, "Bad sequenceNumber"};
}
if (new_metadata->hasAbsCaptureTime() !=
current_metadata->hasAbsCaptureTime() ||
(new_metadata->hasAbsCaptureTime() &&
current_metadata->absCaptureTime() != new_metadata->absCaptureTime())) {
return SetMetadataValidationOutcome{false, "Bad absoluteCaptureTime"};
}
if (!new_metadata->hasRtpTimestamp()) {
return SetMetadataValidationOutcome{false, "Bad rtpTimestamp"};
}
return SetMetadataValidationOutcome{true, String()};
}
} // namespace
RTCEncodedAudioFrame* RTCEncodedAudioFrame::Create(
RTCEncodedAudioFrame* original_frame,
ExceptionState& exception_state) {
return RTCEncodedAudioFrame::Create(original_frame, nullptr, exception_state);
}
RTCEncodedAudioFrame* RTCEncodedAudioFrame::Create(
RTCEncodedAudioFrame* original_frame,
RTCEncodedAudioFrameMetadata* new_metadata,
ExceptionState& exception_state) {
RTCEncodedAudioFrame* new_frame;
if (original_frame) {
new_frame = MakeGarbageCollected<RTCEncodedAudioFrame>(
original_frame->Delegate()->CloneWebRtcFrame());
} else {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidAccessError,
"Cannot create a new AudioFrame: input Audioframe is empty.");
return nullptr;
}
if (new_metadata) {
base::expected<void, String> set_metadata =
new_frame->SetMetadata(new_metadata);
if (!set_metadata.has_value()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError,
"Cannot create a new AudioFrame: " + set_metadata.error());
return nullptr;
}
}
return new_frame;
}
RTCEncodedAudioFrame::RTCEncodedAudioFrame(
std::unique_ptr<webrtc::TransformableAudioFrameInterface>
webrtc_audio_frame)
: delegate_(base::MakeRefCounted<RTCEncodedAudioFrameDelegate>(
std::move(webrtc_audio_frame),
webrtc_audio_frame ? webrtc_audio_frame->GetContributingSources()
: Vector<uint32_t>(),
webrtc_audio_frame ? webrtc_audio_frame->SequenceNumber()
: std::nullopt)) {}
RTCEncodedAudioFrame::RTCEncodedAudioFrame(
scoped_refptr<RTCEncodedAudioFrameDelegate> delegate)
: RTCEncodedAudioFrame(delegate->CloneWebRtcFrame()) {}
uint32_t RTCEncodedAudioFrame::timestamp() const {
return delegate_->RtpTimestamp();
}
DOMArrayBuffer* RTCEncodedAudioFrame::data() const {
if (!frame_data_) {
frame_data_ = delegate_->CreateDataBuffer();
}
return frame_data_.Get();
}
RTCEncodedAudioFrameMetadata* RTCEncodedAudioFrame::getMetadata() const {
RTCEncodedAudioFrameMetadata* metadata =
RTCEncodedAudioFrameMetadata::Create();
if (delegate_->Ssrc()) {
metadata->setSynchronizationSource(*delegate_->Ssrc());
}
metadata->setContributingSources(delegate_->ContributingSources());
if (delegate_->PayloadType()) {
metadata->setPayloadType(*delegate_->PayloadType());
}
if (delegate_->SequenceNumber()) {
metadata->setSequenceNumber(*delegate_->SequenceNumber());
}
if (delegate_->AbsCaptureTime()) {
metadata->setAbsCaptureTime(*delegate_->AbsCaptureTime());
}
metadata->setRtpTimestamp(delegate_->RtpTimestamp());
if (delegate_->MimeType()) {
metadata->setMimeType(WTF::String::FromUTF8(*delegate_->MimeType()));
}
return metadata;
}
base::expected<void, String> RTCEncodedAudioFrame::SetMetadata(
const RTCEncodedAudioFrameMetadata* metadata) {
SetMetadataValidationOutcome validation =
IsAllowedSetMetadataChange(getMetadata(), metadata);
if (!validation.allowed) {
return base::unexpected(
"Invalid modification of RTCEncodedAudioFrameMetadata. " +
validation.error_msg);
}
return delegate_->SetRtpTimestamp(metadata->rtpTimestamp());
}
void RTCEncodedAudioFrame::setMetadata(RTCEncodedAudioFrameMetadata* metadata,
ExceptionState& exception_state) {
base::expected<void, String> set_metadata = SetMetadata(metadata);
if (!set_metadata.has_value()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError,
"Cannot setMetadata: " + set_metadata.error());
}
}
void RTCEncodedAudioFrame::setData(DOMArrayBuffer* data) {
frame_data_ = data;
}
String RTCEncodedAudioFrame::toString() const {
StringBuilder sb;
sb.Append("RTCEncodedAudioFrame{rtpTimestamp: ");
sb.AppendNumber(delegate_->RtpTimestamp());
sb.Append(", size: ");
sb.AppendNumber(data() ? data()->ByteLength() : 0);
sb.Append("}");
return sb.ToString();
}
void RTCEncodedAudioFrame::SyncDelegate() const {
delegate_->SetData(frame_data_);
}
scoped_refptr<RTCEncodedAudioFrameDelegate> RTCEncodedAudioFrame::Delegate()
const {
SyncDelegate();
return delegate_;
}
std::unique_ptr<webrtc::TransformableAudioFrameInterface>
RTCEncodedAudioFrame::PassWebRtcFrame() {
SyncDelegate();
return delegate_->PassWebRtcFrame();
}
void RTCEncodedAudioFrame::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
visitor->Trace(frame_data_);
}
} // namespace blink