Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "CubebInputStream.h"
#include "AudioSampleFormat.h"
#include "mozilla/Logging.h"
namespace mozilla {
extern mozilla::LazyLogModule gMediaTrackGraphLog;
#ifdef LOG_INTERNAL
# undef LOG_INTERNAL
#endif // LOG_INTERNAL
#define LOG_INTERNAL(level, msg, ...) \
MOZ_LOG(gMediaTrackGraphLog, LogLevel::level, (msg, ##__VA_ARGS__))
#ifdef LOG
# undef LOG
#endif // LOG
#define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
#ifdef LOGE
# undef LOGE
#endif // LOGE
#define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
#define InvokeCubebWithLog(func, ...) \
({ \
int _retval; \
_retval = InvokeCubeb(func, ##__VA_ARGS__); \
if (_retval == CUBEB_OK) { \
LOG("CubebInputStream %p: %s for stream %p was successful", this, #func, \
mStream.get()); \
} else { \
LOGE("CubebInputStream %p: %s for stream %p was failed. Error %d", this, \
#func, mStream.get(), _retval); \
} \
_retval; \
})
static cubeb_stream_params CreateStreamInitParams(uint32_t aChannels,
uint32_t aRate,
bool aIsVoice) {
cubeb_stream_params params;
params.format = CubebUtils::ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
params.rate = aRate;
params.channels = aChannels;
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_INPUT);
if (aIsVoice) {
params.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE);
}
return params;
}
void CubebInputStream::CubebDestroyPolicy::operator()(
cubeb_stream* aStream) const {
int r = cubeb_stream_register_device_changed_callback(aStream, nullptr);
if (r == CUBEB_OK) {
LOG("Unregister device changed callback for %p successfully", aStream);
} else {
LOGE("Fail to unregister device changed callback for %p. Error %d", aStream,
r);
}
cubeb_stream_destroy(aStream);
}
/* static */
UniquePtr<CubebInputStream> CubebInputStream::Create(cubeb_devid aDeviceId,
uint32_t aChannels,
uint32_t aRate,
bool aIsVoice,
Listener* aListener) {
if (!aListener) {
LOGE("No available listener");
return nullptr;
}
RefPtr<CubebUtils::CubebHandle> handle = CubebUtils::GetCubeb();
if (!handle) {
LOGE("No valid cubeb context");
CubebUtils::ReportCubebStreamInitFailure(CubebUtils::GetFirstStream());
return nullptr;
}
cubeb_stream_params params =
CreateStreamInitParams(aChannels, aRate, aIsVoice);
uint32_t latencyFrames = CubebUtils::GetCubebMTGLatencyInFrames(&params);
cubeb_stream* cubebStream = nullptr;
RefPtr<Listener> listener(aListener);
if (int r = CubebUtils::CubebStreamInit(
handle->Context(), &cubebStream, "input-only stream", aDeviceId,
&params, nullptr, nullptr, latencyFrames, DataCallback_s,
StateCallback_s, listener.get());
r != CUBEB_OK) {
CubebUtils::ReportCubebStreamInitFailure(CubebUtils::GetFirstStream());
LOGE("Fail to create a cubeb stream. Error %d", r);
return nullptr;
}
UniquePtr<cubeb_stream, CubebDestroyPolicy> inputStream(cubebStream);
LOG("Create a cubeb stream %p successfully", inputStream.get());
UniquePtr<CubebInputStream> stream(
new CubebInputStream(listener.forget(), std::move(inputStream)));
stream->Init();
return stream;
}
CubebInputStream::CubebInputStream(
already_AddRefed<Listener>&& aListener,
UniquePtr<cubeb_stream, CubebDestroyPolicy>&& aStream)
: mListener(aListener),
mCubeb(CubebUtils::GetCubeb()),
mStream(std::move(aStream)) {
MOZ_ASSERT(mListener);
MOZ_ASSERT(mStream);
}
void CubebInputStream::Init() {
// cubeb_stream_register_device_changed_callback is only supported on macOS
// platform and MockCubeb for now.
InvokeCubebWithLog(cubeb_stream_register_device_changed_callback,
CubebInputStream::DeviceChangedCallback_s);
}
int CubebInputStream::Start() { return InvokeCubebWithLog(cubeb_stream_start); }
int CubebInputStream::Stop() { return InvokeCubebWithLog(cubeb_stream_stop); }
int CubebInputStream::SetProcessingParams(
cubeb_input_processing_params aParams) {
return InvokeCubebWithLog(cubeb_stream_set_input_processing_params, aParams);
}
int CubebInputStream::Latency(uint32_t* aLatencyFrames) {
return InvokeCubebWithLog(cubeb_stream_get_input_latency, aLatencyFrames);
}
template <typename Function, typename... Args>
int CubebInputStream::InvokeCubeb(Function aFunction, Args&&... aArgs) {
MOZ_ASSERT(mStream);
return aFunction(mStream.get(), std::forward<Args>(aArgs)...);
}
/* static */
long CubebInputStream::DataCallback_s(cubeb_stream* aStream, void* aUser,
const void* aInputBuffer,
void* aOutputBuffer, long aFrames) {
MOZ_ASSERT(aUser);
MOZ_ASSERT(aInputBuffer);
MOZ_ASSERT(!aOutputBuffer);
return static_cast<Listener*>(aUser)->DataCallback(aInputBuffer, aFrames);
}
/* static */
void CubebInputStream::StateCallback_s(cubeb_stream* aStream, void* aUser,
cubeb_state aState) {
MOZ_ASSERT(aUser);
static_cast<Listener*>(aUser)->StateCallback(aState);
}
/* static */
void CubebInputStream::DeviceChangedCallback_s(void* aUser) {
MOZ_ASSERT(aUser);
static_cast<Listener*>(aUser)->DeviceChangedCallback();
}
#undef LOG_INTERNAL
#undef LOG
#undef LOGE
} // namespace mozilla