183 lines
6.4 KiB
C++
183 lines
6.4 KiB
C++
/* 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_FMT(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 {}: {} for stream {} was successful", \
|
|
fmt::ptr(this), #func, fmt::ptr(mStream.get())); \
|
|
} else { \
|
|
LOGE("CubebInputStream {}: {} for stream {} was failed. Error {}", \
|
|
fmt::ptr(this), #func, fmt::ptr(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);
|
|
params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
|
|
|
|
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 {} successfully",
|
|
fmt::ptr(aStream));
|
|
} else {
|
|
LOGE("Fail to unregister device changed callback for {}. Error {}",
|
|
fmt::ptr(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 {}", r);
|
|
return nullptr;
|
|
}
|
|
|
|
UniquePtr<cubeb_stream, CubebDestroyPolicy> inputStream(cubebStream);
|
|
|
|
LOG("Create a cubeb stream {} successfully", fmt::ptr(inputStream.get()));
|
|
|
|
UniquePtr<CubebInputStream> stream(new CubebInputStream(
|
|
listener.forget(), handle.forget(), std::move(inputStream)));
|
|
stream->Init();
|
|
return stream;
|
|
}
|
|
|
|
CubebInputStream::CubebInputStream(
|
|
already_AddRefed<Listener> aListener,
|
|
already_AddRefed<CubebUtils::CubebHandle> aCubeb,
|
|
UniquePtr<cubeb_stream, CubebDestroyPolicy>&& aStream)
|
|
: mListener(aListener), mCubeb(aCubeb), 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
|