Commit 77a375aa authored by Scott Nichols's avatar Scott Nichols Committed by Commit Bot

Implementation of the iOS audio player for CRD.

Bug: 611181
Change-Id: Ie1e5d658455ac553ece2417cd3b11e86fbbafe61
Reviewed-on: https://chromium-review.googlesource.com/630336
Commit-Queue: Scott Nichols <nicholss@chromium.org>
Reviewed-by: default avatarYuwei Huang <yuweih@chromium.org>
Cr-Commit-Position: refs/heads/master@{#502943}
parent a3da0b4c
......@@ -40,22 +40,20 @@ AudioPlayerBuffer::AudioFrameRequest::AudioFrameRequest(
: bytes_needed_(bytes_needed),
samples_(samples),
callback_(callback),
bytes_extracted_(0) {}
AudioPlayerBuffer::AudioPlayerBuffer()
: queued_bytes_(0), bytes_consumed_(0), weak_factory_(this) {
ResetQueue();
DETACH_FROM_THREAD(thread_checker_);
}
// TODO(nicholss): Fix the impl for auto_lock when integrating the AudioPlayer.
AudioPlayerBuffer::~AudioPlayerBuffer() {
// base::AutoLock auto_lock(lock_);
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ResetQueue();
}
void AudioPlayerBuffer::AddAudioPacket(std::unique_ptr<AudioPacket> packet) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
CHECK_EQ(1, packet->data_size());
DCHECK_EQ(AudioPacket::ENCODING_RAW, packet->encoding());
DCHECK_NE(AudioPacket::SAMPLING_RATE_INVALID, packet->sampling_rate());
......@@ -67,8 +65,6 @@ void AudioPlayerBuffer::AddAudioPacket(std::unique_ptr<AudioPacket> packet) {
(buffered_channels() * buffered_byes_per_sample()),
0u);
// base::AutoLock auto_lock(lock_);
// Push the new data to the back of the queue.
queued_bytes_ += packet->data(0).size();
queued_packets_.push_back(std::move(packet));
......@@ -89,12 +85,14 @@ void AudioPlayerBuffer::AddAudioPacket(std::unique_ptr<AudioPacket> packet) {
}
void AudioPlayerBuffer::Stop() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ResetQueue();
}
void AudioPlayerBuffer::AsyncGetAudioFrame(uint32_t buffer_size,
void* buffer,
const base::Closure& callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Create an AudioFrameRequest and enqueue it into the buffer.
CHECK_EQ(buffer_size % bytes_per_frame(), 0u);
std::unique_ptr<AudioFrameRequest> audioFrameRequest(
......@@ -105,20 +103,16 @@ void AudioPlayerBuffer::AsyncGetAudioFrame(uint32_t buffer_size,
}
void AudioPlayerBuffer::ResetQueue() {
// lock_.AssertAcquired();
// TODO(nicholss): STLDeleteElements has been removed. Leaks from pointers?
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
queued_packets_.clear();
queued_bytes_ = 0;
bytes_consumed_ = 0;
// TODO(nicholss): STLDeleteElements has been removed. Leaks from pointers?
queued_requests_.clear();
}
uint32_t AudioPlayerBuffer::GetAudioFrame(uint32_t buffer_size, void* buffer) {
// base::AutoLock auto_lock(lock_);
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const size_t bytes_needed = bytes_per_frame();
// Make sure we don't overrun the buffer.
CHECK_EQ(buffer_size, bytes_needed);
......@@ -155,14 +149,9 @@ uint32_t AudioPlayerBuffer::GetAudioFrame(uint32_t buffer_size, void* buffer) {
// This is called when the Frame Request Queue or the Sample Queue is changed.
void AudioPlayerBuffer::ProcessFrameRequestQueue() {
// TODO(nicholss): this function will attempt to copy data from the input
// queue into a pending frame request as it can. If it is able to copy enough
// data from the input queue into a request, it will call that request's
// callback with the request context.
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Get the active request if there is one.
while (!queued_requests_.empty() && !queued_packets_.empty()) {
// base::AutoLock auto_lock(lock_);
AudioFrameRequest* activeRequest = queued_requests_.front().get();
// Copy any available data into the active request up to as much requested
......@@ -220,7 +209,8 @@ uint32_t AudioPlayerBuffer::bytes_per_frame() const {
return buffered_channels() * buffered_byes_per_sample() * samples_per_frame();
}
base::WeakPtr<AudioStreamConsumer> AudioPlayerBuffer::AudioConsumerAsWeakPtr() {
base::WeakPtr<AudioStreamConsumer>
AudioPlayerBuffer::AudioStreamConsumerAsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
......
......@@ -11,7 +11,8 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "remoting/client/audio/async_audio_frame_supplier.h"
#include "remoting/client/audio/audio_stream_consumer.h"
#include "remoting/proto/audio.pb.h"
......@@ -37,7 +38,7 @@ class AudioPlayerBuffer : public AudioStreamConsumer,
// Audio Stream Consumer
void AddAudioPacket(std::unique_ptr<AudioPacket> packet) override;
base::WeakPtr<AudioStreamConsumer> AudioConsumerAsWeakPtr() override;
base::WeakPtr<AudioStreamConsumer> AudioStreamConsumerAsWeakPtr() override;
// Async Audio Frame Supplier
void AsyncGetAudioFrame(uint32_t buffer_size,
......@@ -61,11 +62,6 @@ class AudioPlayerBuffer : public AudioStreamConsumer,
void ResetQueue();
void ProcessFrameRequestQueue();
// Protects |queued_packets_|, |queued_requests_|, |queued_samples_ and
// |bytes_consumed_|. This is necessary to prevent races, because Audio
// Player will call the callback on a separate thread.
base::Lock lock_;
std::list<std::unique_ptr<AudioPacket>> queued_packets_;
int queued_bytes_;
......@@ -74,6 +70,8 @@ class AudioPlayerBuffer : public AudioStreamConsumer,
std::list<std::unique_ptr<AudioFrameRequest>> queued_requests_;
THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<AudioPlayerBuffer> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AudioPlayerBuffer);
......
......@@ -29,7 +29,7 @@ class AudioStreamConsumer : public protocol::AudioStub {
// requested.
virtual void AddAudioPacket(std::unique_ptr<AudioPacket> packet) = 0;
// Get a weak refrence to the audio consumer.
virtual base::WeakPtr<AudioStreamConsumer> AudioConsumerAsWeakPtr() = 0;
virtual base::WeakPtr<AudioStreamConsumer> AudioStreamConsumerAsWeakPtr() = 0;
// AudioStub implementation. Delegates to AddAudioPacket. Used for
// integration with |protocol::AudioStub| API users but AddAudioPacket
......
......@@ -30,13 +30,12 @@ ChromotingClient::ChromotingClient(
ClientContext* client_context,
ClientUserInterface* user_interface,
protocol::VideoRenderer* video_renderer,
base::WeakPtr<protocol::AudioStub> audio_consumer)
: user_interface_(user_interface),
video_renderer_(video_renderer) {
base::WeakPtr<protocol::AudioStub> audio_stream_consumer)
: user_interface_(user_interface), video_renderer_(video_renderer) {
DCHECK(client_context->main_task_runner()->BelongsToCurrentThread());
audio_decode_task_runner_ = client_context->audio_decode_task_runner();
audio_consumer_ = audio_consumer;
audio_stream_consumer_ = audio_stream_consumer;
}
ChromotingClient::~ChromotingClient() {
......@@ -98,8 +97,9 @@ void ChromotingClient::Start(
connection_->set_clipboard_stub(this);
connection_->set_video_renderer(video_renderer_);
if (audio_consumer_) {
connection_->InitializeAudio(audio_decode_task_runner_, audio_consumer_);
if (audio_stream_consumer_) {
connection_->InitializeAudio(audio_decode_task_runner_,
audio_stream_consumer_);
} else {
protocol_config_->DisableAudioChannel();
}
......
......@@ -46,12 +46,12 @@ class ChromotingClient : public SignalStrategy::Listener,
public protocol::ClientStub {
public:
// |client_context|, |user_interface| and |video_renderer| must outlive the
// client. |audio_consumer| may be null, in which case audio will not be
// requested.
// client. |audio_stream_consumer| may be null, in which case audio will not
// be requested.
ChromotingClient(ClientContext* client_context,
ClientUserInterface* user_interface,
protocol::VideoRenderer* video_renderer,
base::WeakPtr<protocol::AudioStub> audio_consumer);
base::WeakPtr<protocol::AudioStub> audio_stream_consumer);
~ChromotingClient() override;
......@@ -122,7 +122,7 @@ class ChromotingClient : public SignalStrategy::Listener,
// The following are not owned by this class.
ClientUserInterface* user_interface_ = nullptr;
protocol::VideoRenderer* video_renderer_ = nullptr;
base::WeakPtr<protocol::AudioStub> audio_consumer_;
base::WeakPtr<protocol::AudioStub> audio_stream_consumer_;
SignalStrategy* signal_strategy_ = nullptr;
std::string host_jid_;
......
......@@ -54,7 +54,7 @@ ChromotingClientRuntime::ChromotingClientRuntime() {
// base::MessageLoop::QuitClosure())
ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->task_runner(),
base::Bind(&base::DoNothing));
audio_task_runner_ = AutoThread::Create("native_audio", ui_task_runner_);
display_task_runner_ = AutoThread::Create("native_disp", ui_task_runner_);
network_task_runner_ = AutoThread::CreateWithType(
"native_net", ui_task_runner_, base::MessageLoop::TYPE_IO);
......
......@@ -54,6 +54,10 @@ class ChromotingClientRuntime {
return network_task_runner_;
}
scoped_refptr<AutoThreadTaskRunner> audio_task_runner() {
return audio_task_runner_;
}
scoped_refptr<AutoThreadTaskRunner> ui_task_runner() {
return ui_task_runner_;
}
......@@ -93,6 +97,7 @@ class ChromotingClientRuntime {
// Longer term we should migrate most of these to background tasks except the
// network thread to TaskScheduler, removing the need for threads.
scoped_refptr<AutoThreadTaskRunner> audio_task_runner_;
scoped_refptr<AutoThreadTaskRunner> display_task_runner_;
scoped_refptr<AutoThreadTaskRunner> network_task_runner_;
scoped_refptr<AutoThreadTaskRunner> file_task_runner_;
......
......@@ -76,6 +76,7 @@ source_set("common_source_set") {
"//remoting/ios:ios_core",
"//remoting/ios/app/resources:assets",
"//remoting/ios/app/settings",
"//remoting/ios/audio",
"//remoting/ios/display",
"//remoting/ios/domain",
"//remoting/ios/mdc",
......
# Copyright 2017 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.
import("//build/config/chrome_build.gni")
import("//build/config/ios/rules.gni")
import("//remoting/build/config/remoting_build.gni")
source_set("audio") {
sources = [
"audio_player_ios.h",
"audio_player_ios.mm",
"audio_player_ios_wrapper.h",
"audio_stream_consumer_proxy.cc",
"audio_stream_consumer_proxy.h",
]
deps = [
"//base",
"//remoting/base",
"//remoting/client/audio",
]
configs += [ "//build/config/compiler:enable_arc" ]
}
// Copyright 2017 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.
#ifndef REMOTING_IOS_AUDIO_AUDIO_PLAYER_IOS_H_
#define REMOTING_IOS_AUDIO_AUDIO_PLAYER_IOS_H_
#import <AudioToolbox/AudioToolbox.h>
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "remoting/base/auto_thread.h"
#include "remoting/client/audio/async_audio_frame_supplier.h"
#include "remoting/client/audio/audio_stream_consumer.h"
namespace remoting {
/*
For iOS, the audio subsystem uses a multi-buffer queue to produce smooth
audio playback. To allow this to work with remoting, we need to add a buffer
that is capable of enqueuing exactly the requested amount of data
asynchronously then calling us back and we will push it into the iOS audio
queue.
*/
class AudioPlayerIos {
public:
static std::unique_ptr<AudioPlayerIos> CreateAudioPlayer(
scoped_refptr<AutoThreadTaskRunner> audio_thread_runner);
AudioPlayerIos(std::unique_ptr<AudioStreamConsumer> audio_stream_consumer,
std::unique_ptr<AsyncAudioFrameSupplier> audio_frame_supplier,
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner);
// Call on the audio thread.
~AudioPlayerIos();
base::WeakPtr<AudioStreamConsumer> GetAudioStreamConsumer();
void Start();
void Stop();
private:
static const uint32_t kOutputBuffers = 8;
enum AudioPlayerState {
UNALLOCATED,
STOPPING,
STOPPED,
PRIMING,
PLAYING,
UNDERRUN,
UNKNOWN
};
static void AudioEngineOutputBufferCallback(void* instance,
AudioQueueRef outAQ,
AudioQueueBufferRef samples);
// Audio Thread versions of public functions.
void StartOnAudioThread();
void StopOnAudioThread();
// Audio Thread private functions.
void Prime(AudioQueueBufferRef output_buffer, bool was_dequeued);
void Pump(AudioQueueBufferRef output_buffer);
void PrimeQueue();
void StartPlayback();
void AsyncGetAudioFrameCallback(const void* context, const void* samples);
bool GenerateOutputBuffers(uint32_t bytesPerFrame);
bool GenerateOutputQueue(void* context);
AudioStreamBasicDescription GenerateStreamFormat();
AudioPlayerState state_;
uint32_t priming_frames_needed_count_;
AudioQueueRef output_queue_;
AudioQueueBufferRef output_buffers_[kOutputBuffers];
std::unique_ptr<AudioStreamConsumer> audio_stream_consumer_;
std::unique_ptr<AsyncAudioFrameSupplier> audio_frame_supplier_;
uint32_t enqueued_frames_count_;
// Task runner on which the |audio_frame_supplier_| is running.
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
base::WeakPtrFactory<AudioPlayerIos> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AudioPlayerIos);
};
} // namespace remoting
#endif // REMOTING_IOS_AUDIO_AUDIO_PLAYER_IOS_H_
// Copyright 2017 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import <AudioToolbox/AudioToolbox.h>
#import <Foundation/Foundation.h>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "remoting/base/auto_thread.h"
#include "remoting/client/audio/audio_player_buffer.h"
#include "remoting/ios/audio/audio_player_ios.h"
#include "remoting/ios/audio/audio_stream_consumer_proxy.h"
namespace remoting {
std::unique_ptr<AudioPlayerIos> AudioPlayerIos::CreateAudioPlayer(
scoped_refptr<AutoThreadTaskRunner> audio_thread_runner) {
AudioPlayerBuffer* buffer = new AudioPlayerBuffer();
std::unique_ptr<AsyncAudioFrameSupplier> supplier =
(std::unique_ptr<AsyncAudioFrameSupplier>)base::WrapUnique(buffer);
std::unique_ptr<AudioStreamConsumer> consumer_proxy =
AudioStreamConsumerProxy::Create(audio_thread_runner,
buffer->AudioStreamConsumerAsWeakPtr());
return base::MakeUnique<AudioPlayerIos>(
std::move(consumer_proxy), std::move(supplier), audio_thread_runner);
}
// public
AudioPlayerIos::AudioPlayerIos(
std::unique_ptr<AudioStreamConsumer> audio_stream_consumer,
std::unique_ptr<AsyncAudioFrameSupplier> audio_frame_supplier,
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner)
: state_(UNALLOCATED),
enqueued_frames_count_(0),
audio_task_runner_(audio_task_runner),
weak_factory_(this) {
audio_stream_consumer_ = std::move(audio_stream_consumer);
audio_frame_supplier_ = std::move(audio_frame_supplier);
}
AudioPlayerIos::~AudioPlayerIos() {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
StopOnAudioThread();
// Disposing of an audio queue also disposes of its resources, including its
// buffers.
AudioQueueDispose(output_queue_, true /* Immediate */);
for (uint32_t i = 0; i < kOutputBuffers; i++) {
output_buffers_[i] = nullptr;
}
}
base::WeakPtr<AudioStreamConsumer> AudioPlayerIos::GetAudioStreamConsumer() {
return audio_stream_consumer_->AudioStreamConsumerAsWeakPtr();
}
void AudioPlayerIos::Start() {
audio_task_runner_->PostTask(FROM_HERE,
base::Bind(&AudioPlayerIos::StartOnAudioThread,
weak_factory_.GetWeakPtr()));
}
void AudioPlayerIos::Stop() {
audio_task_runner_->PostTask(FROM_HERE,
base::Bind(&AudioPlayerIos::StopOnAudioThread,
weak_factory_.GetWeakPtr()));
}
void AudioPlayerIos::Prime(AudioQueueBufferRef output_buffer,
bool was_dequeued) {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
if (state_ == STOPPING) {
return;
}
if (was_dequeued) {
--enqueued_frames_count_;
}
audio_frame_supplier_->AsyncGetAudioFrame(
audio_frame_supplier_->bytes_per_frame(),
reinterpret_cast<void*>(output_buffer->mAudioData),
base::Bind(&AudioPlayerIos::AsyncGetAudioFrameCallback,
weak_factory_.GetWeakPtr(),
reinterpret_cast<void*>(output_buffer),
reinterpret_cast<void*>(output_buffer->mAudioData)));
if (state_ == PLAYING && enqueued_frames_count_ == 0) {
state_ = UNDERRUN;
StopOnAudioThread();
// We have a bunch of pending requests so we are not stopped, but really in
// PRIMING. Avoid re-priming the queue.
state_ = PRIMING;
priming_frames_needed_count_ = kOutputBuffers;
}
}
void AudioPlayerIos::Pump(AudioQueueBufferRef output_buffer) {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
OSStatus err = AudioQueueEnqueueBuffer(output_queue_, output_buffer, 0, nil);
if (err) {
LOG(FATAL) << "AudioQueueEnqueueBuffer: " << err;
} else {
++enqueued_frames_count_;
}
if (state_ == PRIMING) {
--priming_frames_needed_count_;
if (priming_frames_needed_count_ == 0) {
audio_task_runner_->PostTask(FROM_HERE,
base::Bind(&AudioPlayerIos::StartPlayback,
weak_factory_.GetWeakPtr()));
}
}
}
// private
// static, AudioQueue output queue callback.
void AudioPlayerIos::AudioEngineOutputBufferCallback(
void* instance,
AudioQueueRef outAQ,
AudioQueueBufferRef samples) {
AudioPlayerIos* player = (AudioPlayerIos*)instance;
player->Prime(samples, true);
}
void AudioPlayerIos::StartOnAudioThread() {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
switch (state_) {
case STOPPING:
// Nothing to do.
return;
case UNALLOCATED:
GenerateOutputQueue((void*)this);
GenerateOutputBuffers(audio_frame_supplier_->bytes_per_frame());
state_ = STOPPED;
// Fall-through intended.
case STOPPED:
PrimeQueue();
return;
case PRIMING:
// Nothing to do.
return;
case PLAYING:
// Nothing to do.
return;
case UNDERRUN:
// Nothing to do.
return;
default:
LOG(FATAL) << "Audio Player: Unknown State.";
}
}
void AudioPlayerIos::StopOnAudioThread() {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
state_ = STOPPING;
if (output_queue_) {
AudioQueueStop(output_queue_, YES);
}
state_ = STOPPED;
}
// Should only be called while |state_| is STOPPED
void AudioPlayerIos::PrimeQueue() {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
if (state_ != STOPPED) {
return;
}
state_ = PRIMING;
priming_frames_needed_count_ = kOutputBuffers;
for (uint32_t i = 0; i < kOutputBuffers; i++) {
Prime(output_buffers_[i], false);
}
}
void AudioPlayerIos::StartPlayback() {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
OSStatus err;
err = AudioQueueStart(output_queue_, nil);
if (err) {
LOG(FATAL) << "AudioQueueStart: " << err;
state_ = UNKNOWN;
return;
}
state_ = PLAYING;
return;
}
void AudioPlayerIos::AsyncGetAudioFrameCallback(const void* context,
const void* samples) {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
AudioQueueBufferRef output_buffer = (AudioQueueBufferRef)context;
output_buffer->mAudioDataByteSize = audio_frame_supplier_->bytes_per_frame();
Pump(output_buffer);
}
bool AudioPlayerIos::GenerateOutputBuffers(uint32_t bytesPerFrame) {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
OSStatus err;
for (uint32_t i = 0; i < kOutputBuffers; i++) {
err = AudioQueueAllocateBuffer(output_queue_, bytesPerFrame,
&output_buffers_[i]);
if (err) {
LOG(FATAL) << "AudioQueueAllocateBuffer[" << i << "] : " << err;
return false;
}
}
return true;
}
bool AudioPlayerIos::GenerateOutputQueue(void* context) {
DCHECK(audio_task_runner_->BelongsToCurrentThread());
// Set up stream format fields
AudioStreamBasicDescription streamFormat = GenerateStreamFormat();
OSStatus err;
err = AudioQueueNewOutput(&streamFormat, AudioEngineOutputBufferCallback,
context, CFRunLoopGetCurrent(),
kCFRunLoopCommonModes, 0, &output_queue_);
if (err) {
LOG(FATAL) << "AudioQueueNewOutput: " << err;
return false;
}
return true;
}
AudioStreamBasicDescription AudioPlayerIos::GenerateStreamFormat() {
// Set up stream format fields
// TODO(nicholss): does this all needs to be generated dynamicly?
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = 48000;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
streamFormat.mBitsPerChannel = 16;
streamFormat.mChannelsPerFrame = 2;
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mFramesPerPacket = 1;
streamFormat.mReserved = 0;
return streamFormat;
}
} // namespace remoting
// Copyright 2017 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 "remoting/ios/audio/audio_stream_consumer_proxy.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
namespace remoting {
// Runs an instance of |HostWindow| on the |audio_task_runner_| thread.
class AudioStreamConsumerProxy::Core {
public:
Core(base::WeakPtr<AudioStreamConsumer> audio_stream_consumer);
~Core();
void AddAudioPacket(std::unique_ptr<AudioPacket> packet);
base::WeakPtr<AudioStreamConsumer> AudioStreamConsumerAsWeakPtr();
base::WeakPtr<Core> GetWeakPtr();
private:
// The wrapped |AudioStreamConsumer| instance running on the
// |audio_task_runner_| thread.
base::WeakPtr<AudioStreamConsumer> audio_stream_consumer_;
base::WeakPtrFactory<Core> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Core);
};
std::unique_ptr<AudioStreamConsumerProxy> AudioStreamConsumerProxy::Create(
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
base::WeakPtr<AudioStreamConsumer> audio_stream_consumer) {
std::unique_ptr<Core> core(new Core(std::move(audio_stream_consumer)));
std::unique_ptr<AudioStreamConsumerProxy> result(
new AudioStreamConsumerProxy(audio_task_runner, std::move(core)));
return result;
}
AudioStreamConsumerProxy::AudioStreamConsumerProxy(
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
std::unique_ptr<Core> core)
: audio_task_runner_(audio_task_runner),
core_(std::move(core)),
weak_factory_(this) {}
AudioStreamConsumerProxy::~AudioStreamConsumerProxy() {}
void AudioStreamConsumerProxy::AddAudioPacket(
std::unique_ptr<AudioPacket> packet) {
audio_task_runner_->PostTask(
FROM_HERE, base::Bind(&Core::AddAudioPacket, core_->GetWeakPtr(),
base::Passed(&packet)));
}
base::WeakPtr<AudioStreamConsumer>
AudioStreamConsumerProxy::AudioStreamConsumerAsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
AudioStreamConsumerProxy::Core::Core(
base::WeakPtr<AudioStreamConsumer> audio_stream_consumer)
: audio_stream_consumer_(audio_stream_consumer), weak_factory_(this) {}
AudioStreamConsumerProxy::Core::~Core() {}
void AudioStreamConsumerProxy::Core::AddAudioPacket(
std::unique_ptr<AudioPacket> packet) {
audio_stream_consumer_->AddAudioPacket(std::move(packet));
}
base::WeakPtr<AudioStreamConsumerProxy::Core>
AudioStreamConsumerProxy::Core::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace remoting
// Copyright 2017 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.
#ifndef REMOTING_IOS_AUDIO_AUDIO_STREAM_CONSUMER_PROXY_H_
#define REMOTING_IOS_AUDIO_AUDIO_STREAM_CONSUMER_PROXY_H_
#include <cstdint>
#include <list>
#include <memory>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "remoting/client/audio/audio_stream_consumer.h"
#include "remoting/proto/audio.pb.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace remoting {
class AudioStreamConsumer;
// Takes an instance of |AudioConsumer| and runs it on the |audio_task_runner|
// thread.
class AudioStreamConsumerProxy : public AudioStreamConsumer {
public:
static std::unique_ptr<AudioStreamConsumerProxy> Create(
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
base::WeakPtr<AudioStreamConsumer> audio_stream_consumer);
~AudioStreamConsumerProxy() override;
// AudioStreamConsumer overrides.
void AddAudioPacket(std::unique_ptr<AudioPacket> packet) override;
base::WeakPtr<AudioStreamConsumer> AudioStreamConsumerAsWeakPtr() override;
private:
class Core;
AudioStreamConsumerProxy(
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
std::unique_ptr<Core> core);
// Task runner on which |core_| is called.
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
// All thread switching logic is implemented in the |Core| class.
std::unique_ptr<Core> core_;
base::WeakPtrFactory<AudioStreamConsumerProxy> weak_factory_;
};
} // namespace remoting
#endif // REMOTING_IOS_AUDIO_AUDIO_STREAM_CONSUMER_PROXY_H_
......@@ -13,6 +13,7 @@
#import "base/mac/bind_objc_block.h"
#import "ios/third_party/material_components_ios/src/components/Dialogs/src/MaterialDialogs.h"
#import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
#import "remoting/ios/audio/audio_player_ios.h"
#import "remoting/ios/display/gl_display_handler.h"
#import "remoting/ios/domain/client_session_details.h"
#import "remoting/ios/domain/host_info.h"
......@@ -42,7 +43,6 @@ NSString* const kHostSessionPin = @"kHostSessionPin";
@interface RemotingClient () {
remoting::ChromotingClientRuntime* _runtime;
std::unique_ptr<remoting::ChromotingSession> _session;
std::unique_ptr<remoting::RemotingClientSessonDelegate> _sessonDelegate;
ClientSessionDetails* _sessionDetails;
// Call _secretFetchedCallback on the network thread.
......@@ -50,6 +50,8 @@ NSString* const kHostSessionPin = @"kHostSessionPin";
std::unique_ptr<remoting::RendererProxy> _renderer;
std::unique_ptr<remoting::GestureInterpreter> _gestureInterpreter;
std::unique_ptr<remoting::KeyboardInterpreter> _keyboardInterpreter;
std::unique_ptr<remoting::AudioPlayerIos> _audioPlayer;
std::unique_ptr<remoting::ChromotingSession> _session;
}
@end
......@@ -141,22 +143,23 @@ NSString* const kHostSessionPin = @"kHostSessionPin";
}];
});
// TODO(nicholss): Add audio support to iOS.
base::WeakPtr<remoting::protocol::AudioStub> audioPlayer = nullptr;
_audioPlayer = remoting::AudioPlayerIos::CreateAudioPlayer(
_runtime->audio_task_runner());
_displayHandler = [[GlDisplayHandler alloc] init];
_displayHandler.delegate = self;
_session.reset(new remoting::ChromotingSession(
_sessonDelegate->GetWeakPtr(), [_displayHandler CreateCursorShapeStub],
[_displayHandler CreateVideoRenderer], audioPlayer, info,
client_auth_config));
[_displayHandler CreateVideoRenderer],
_audioPlayer->GetAudioStreamConsumer(), info, client_auth_config));
_renderer = [_displayHandler CreateRendererProxy];
_gestureInterpreter.reset(
new remoting::GestureInterpreter(_renderer.get(), _session.get()));
_keyboardInterpreter.reset(new remoting::KeyboardInterpreter(_session.get()));
_session->Connect();
_audioPlayer->Start();
}
- (void)disconnectFromHost {
......@@ -164,8 +167,13 @@ NSString* const kHostSessionPin = @"kHostSessionPin";
_session->Disconnect();
_runtime->network_task_runner()->DeleteSoon(FROM_HERE, _session.release());
}
_displayHandler = nil;
if (_audioPlayer) {
_runtime->audio_task_runner()->DeleteSoon(FROM_HERE,
_audioPlayer.release());
}
// This needs to be deleted on the display thread since GlDisplayHandler binds
// its WeakPtrFactory to the display thread.
// TODO(yuweih): Ideally this constraint can be removed once we allow
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment