blob: 80af6b909646656be523d096d76d2f613e354425 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// 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/mediarecorder/audio_track_recorder.h"
#include "base/macros.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_parameters.h"
#include "media/base/bind_to_current_loop.h"
#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
#include "third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h"
#include "third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
// Note that this code follows the Chrome media convention of defining a "frame"
// as "one multi-channel sample" as opposed to another common definition meaning
// "a chunk of samples". Here this second definition of "frame" is called a
// "buffer"; so what might be called "frame duration" is instead "buffer
// duration", and so on.
namespace WTF {
template <>
struct CrossThreadCopier<media::AudioParameters> {
STATIC_ONLY(CrossThreadCopier);
using Type = media::AudioParameters;
static Type Copy(Type pointer) { return pointer; }
};
} // namespace WTF
namespace blink {
AudioTrackRecorder::CodecId AudioTrackRecorder::GetPreferredCodecId() {
return CodecId::OPUS;
}
AudioTrackRecorder::AudioTrackRecorder(
CodecId codec,
MediaStreamComponent* track,
OnEncodedAudioCB on_encoded_audio_cb,
base::OnceClosure on_track_source_ended_cb,
int32_t bits_per_second,
BitrateMode bitrate_mode)
: TrackRecorder(std::move(on_track_source_ended_cb)),
track_(track),
encoder_(CreateAudioEncoder(codec,
std::move(on_encoded_audio_cb),
bits_per_second,
bitrate_mode)),
encoder_thread_(Thread::CreateThread(
ThreadCreationParams(ThreadType::kAudioEncoderThread))),
encoder_task_runner_(encoder_thread_->GetTaskRunner()) {
DCHECK(IsMainThread());
DCHECK(track_);
DCHECK(track_->Source()->GetType() == MediaStreamSource::kTypeAudio);
// Connect the source provider to the track as a sink.
ConnectToTrack();
}
AudioTrackRecorder::~AudioTrackRecorder() {
DCHECK(IsMainThread());
DisconnectFromTrack();
}
// Creates an audio encoder from the codec. Returns nullptr if the codec is
// invalid.
scoped_refptr<AudioTrackEncoder> AudioTrackRecorder::CreateAudioEncoder(
CodecId codec,
OnEncodedAudioCB on_encoded_audio_cb,
int32_t bits_per_second,
BitrateMode bitrate_mode) {
if (codec == CodecId::PCM) {
return base::MakeRefCounted<AudioTrackPcmEncoder>(
media::BindToCurrentLoop(std::move(on_encoded_audio_cb)));
}
// All other paths will use the AudioTrackOpusEncoder.
return base::MakeRefCounted<AudioTrackOpusEncoder>(
media::BindToCurrentLoop(std::move(on_encoded_audio_cb)), bits_per_second,
bitrate_mode == BitrateMode::VARIABLE);
}
void AudioTrackRecorder::OnSetFormat(const media::AudioParameters& params) {
// If the source is restarted, might have changed to another capture thread.
DETACH_FROM_THREAD(capture_thread_checker_);
DCHECK_CALLED_ON_VALID_THREAD(capture_thread_checker_);
PostCrossThreadTask(
*encoder_task_runner_.get(), FROM_HERE,
CrossThreadBindOnce(&AudioTrackEncoder::OnSetFormat, encoder_, params));
}
void AudioTrackRecorder::OnData(const media::AudioBus& audio_bus,
base::TimeTicks capture_time) {
DCHECK_CALLED_ON_VALID_THREAD(capture_thread_checker_);
DCHECK(!capture_time.is_null());
std::unique_ptr<media::AudioBus> audio_data =
media::AudioBus::Create(audio_bus.channels(), audio_bus.frames());
audio_bus.CopyTo(audio_data.get());
PostCrossThreadTask(
*encoder_task_runner_.get(), FROM_HERE,
CrossThreadBindOnce(&AudioTrackEncoder::EncodeAudio, encoder_,
std::move(audio_data), capture_time));
}
void AudioTrackRecorder::Pause() {
DCHECK(IsMainThread());
DCHECK(encoder_);
PostCrossThreadTask(
*encoder_task_runner_.get(), FROM_HERE,
CrossThreadBindOnce(&AudioTrackEncoder::set_paused, encoder_, true));
}
void AudioTrackRecorder::Resume() {
DCHECK(IsMainThread());
DCHECK(encoder_);
PostCrossThreadTask(
*encoder_task_runner_.get(), FROM_HERE,
CrossThreadBindOnce(&AudioTrackEncoder::set_paused, encoder_, false));
}
void AudioTrackRecorder::ConnectToTrack() {
auto* audio_track =
static_cast<MediaStreamAudioTrack*>(track_->GetPlatformTrack());
DCHECK(audio_track);
audio_track->AddSink(this);
}
void AudioTrackRecorder::DisconnectFromTrack() {
auto* audio_track =
static_cast<MediaStreamAudioTrack*>(track_->GetPlatformTrack());
DCHECK(audio_track);
audio_track->RemoveSink(this);
}
} // namespace blink