/* -*- 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(¶ms);
cubeb_stream* cubebStream = nullptr;
RefPtr<Listener> listener(aListener);
if (
int r = CubebUtils::CubebStreamInit(
handle->Context(), &cubebStream,
"input-only stream", aDeviceId,
¶ms, 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