feat(microphone): add input device listing API
This commit is contained in:
parent
aa69aa78ef
commit
36c74af588
@ -1,4 +1,7 @@
|
|||||||
add_library(ffmpeg_microphone SHARED src/ffmpeg_microphone.cpp)
|
add_library(ffmpeg_microphone SHARED
|
||||||
|
src/ffmpeg_microphone.cpp
|
||||||
|
src/microphone_device_discovery.cpp
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(ffmpeg_microphone PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(ffmpeg_microphone PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,74 @@
|
|||||||
|
#include "devices/microphone/microphone_device_discovery.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavdevice/avdevice.h>
|
||||||
|
#include <libavutil/error.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace cmvr::device {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string avErrorToString(int error)
|
||||||
|
{
|
||||||
|
char buffer[AV_ERROR_MAX_STRING_SIZE] = {0};
|
||||||
|
av_strerror(error, buffer, sizeof(buffer));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DeviceListDeleter {
|
||||||
|
void operator()(AVDeviceInfoList* list) const
|
||||||
|
{
|
||||||
|
avdevice_free_list_devices(&list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::vector<MicrophoneInputDeviceInfo> listAvailableMicrophoneInputDevices()
|
||||||
|
{
|
||||||
|
constexpr const char* kBackend = "alsa";
|
||||||
|
|
||||||
|
avdevice_register_all();
|
||||||
|
const AVInputFormat* input_format = av_find_input_format(kBackend);
|
||||||
|
if (!input_format) {
|
||||||
|
throw std::runtime_error("FFmpeg ALSA input backend is unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
AVDeviceInfoList* raw_device_list = nullptr;
|
||||||
|
const int result = avdevice_list_input_sources(
|
||||||
|
input_format, nullptr, nullptr, &raw_device_list);
|
||||||
|
std::unique_ptr<AVDeviceInfoList, DeviceListDeleter> device_list(raw_device_list);
|
||||||
|
if (result < 0) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to list ALSA microphone devices: " + avErrorToString(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<MicrophoneInputDeviceInfo> devices;
|
||||||
|
if (!device_list) {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
devices.reserve(static_cast<std::size_t>(device_list->nb_devices));
|
||||||
|
|
||||||
|
for (int index = 0; index < device_list->nb_devices; ++index) {
|
||||||
|
const AVDeviceInfo* source = device_list->devices[index];
|
||||||
|
if (!source || !source->device_name || source->device_name[0] == '\0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MicrophoneInputDeviceInfo device;
|
||||||
|
device.input_device = source->device_name;
|
||||||
|
device.display_name = source->device_description && source->device_description[0] != '\0'
|
||||||
|
? source->device_description
|
||||||
|
: source->device_name;
|
||||||
|
device.backend = kBackend;
|
||||||
|
device.is_default = index == device_list->default_device;
|
||||||
|
devices.push_back(std::move(device));
|
||||||
|
}
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cmvr::device
|
||||||
19
cmvr-es/devices/microphone/microphone_device_discovery.h
Normal file
19
cmvr-es/devices/microphone/microphone_device_discovery.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace cmvr::device {
|
||||||
|
|
||||||
|
struct MicrophoneInputDeviceInfo {
|
||||||
|
std::string input_device;
|
||||||
|
std::string display_name;
|
||||||
|
std::string backend;
|
||||||
|
bool is_default{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the capture devices that can be used as microphone inputs.
|
||||||
|
// Throws std::runtime_error when the platform backend cannot enumerate devices.
|
||||||
|
std::vector<MicrophoneInputDeviceInfo> listAvailableMicrophoneInputDevices();
|
||||||
|
|
||||||
|
} // namespace cmvr::device
|
||||||
@ -18,6 +18,7 @@ target_link_libraries(service PRIVATE
|
|||||||
cmvr_es::proto
|
cmvr_es::proto
|
||||||
osqp
|
osqp
|
||||||
cmvr_es::device_manager
|
cmvr_es::device_manager
|
||||||
|
cmvr_es::device::microphone
|
||||||
cmvr_es::task_manager
|
cmvr_es::task_manager
|
||||||
cmvr_es::algorithms::controller
|
cmvr_es::algorithms::controller
|
||||||
cmvr_es::task
|
cmvr_es::task
|
||||||
|
|||||||
@ -17,6 +17,10 @@ namespace cmvr::service
|
|||||||
public:
|
public:
|
||||||
gRPCMicroPhoneServiceImpl();
|
gRPCMicroPhoneServiceImpl();
|
||||||
~gRPCMicroPhoneServiceImpl() override = default;
|
~gRPCMicroPhoneServiceImpl() override = default;
|
||||||
|
grpc::Status ListDevices(
|
||||||
|
grpc::ServerContext* context,
|
||||||
|
const api::ListMicrophoneDevicesCommand_Request* request,
|
||||||
|
api::ListMicrophoneDevicesCommand_Feedback* response) override;
|
||||||
grpc::Status GetStatus(grpc::ServerContext* context, const api::GetMicStateCommand_Request* request,api::GetMicStateCommand_Feedback* response) override;
|
grpc::Status GetStatus(grpc::ServerContext* context, const api::GetMicStateCommand_Request* request,api::GetMicStateCommand_Feedback* response) override;
|
||||||
grpc::Status StartRecord(grpc::ServerContext* context, const api::StartMicRecordingCommand_Request* request,api::StartMicRecordingCommand_Feedback* response) override;
|
grpc::Status StartRecord(grpc::ServerContext* context, const api::StartMicRecordingCommand_Request* request,api::StartMicRecordingCommand_Feedback* response) override;
|
||||||
grpc::Status StopRecord(grpc::ServerContext* context, const api::StopMicRecordingCommand_Request* request,api::StopMicRecordingCommand_Feedback* response) override;
|
grpc::Status StopRecord(grpc::ServerContext* context, const api::StopMicRecordingCommand_Request* request,api::StopMicRecordingCommand_Feedback* response) override;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include "common/base/logging/logger.h"
|
#include "common/base/logging/logger.h"
|
||||||
|
#include "devices/microphone/microphone_device_discovery.h"
|
||||||
#include "manager/media_source_hub/include/device_media_source_adapter.h"
|
#include "manager/media_source_hub/include/device_media_source_adapter.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -28,6 +29,31 @@ grpc::Status failResponse(ResponseT* response, const std::string& message) {
|
|||||||
|
|
||||||
gRPCMicroPhoneServiceImpl::gRPCMicroPhoneServiceImpl(): dmgr_(DeviceManager::getInstance()) {}
|
gRPCMicroPhoneServiceImpl::gRPCMicroPhoneServiceImpl(): dmgr_(DeviceManager::getInstance()) {}
|
||||||
|
|
||||||
|
grpc::Status gRPCMicroPhoneServiceImpl::ListDevices(
|
||||||
|
grpc::ServerContext* context,
|
||||||
|
const api::ListMicrophoneDevicesCommand_Request* request,
|
||||||
|
api::ListMicrophoneDevicesCommand_Feedback* response) {
|
||||||
|
(void)context;
|
||||||
|
(void)request;
|
||||||
|
try {
|
||||||
|
const auto devices = listAvailableMicrophoneInputDevices();
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
auto* item = response->add_devices();
|
||||||
|
item->set_input_device(device.input_device);
|
||||||
|
item->set_display_name(device.display_name);
|
||||||
|
item->set_backend(device.backend);
|
||||||
|
item->set_is_default(device.is_default);
|
||||||
|
}
|
||||||
|
response->mutable_header()->set_success(true);
|
||||||
|
setCurrentTimestamp(response->mutable_header()->mutable_timestamp());
|
||||||
|
CMVR_LOG(INFO) << "[gRPCMicroPhoneServiceImpl] (ListDevices): found "
|
||||||
|
<< devices.size() << " microphone input device(s)";
|
||||||
|
return grpc::Status::OK;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return failResponse(response, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
grpc::Status gRPCMicroPhoneServiceImpl::GetStatus(grpc::ServerContext* context,
|
grpc::Status gRPCMicroPhoneServiceImpl::GetStatus(grpc::ServerContext* context,
|
||||||
const api::GetMicStateCommand_Request* request, api::GetMicStateCommand_Feedback* response) {
|
const api::GetMicStateCommand_Request* request, api::GetMicStateCommand_Feedback* response) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -12,6 +12,22 @@ message MicState {
|
|||||||
string error_message = 5;
|
string error_message = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MicrophoneInputDevice {
|
||||||
|
string input_device = 1;
|
||||||
|
string display_name = 2;
|
||||||
|
string backend = 3;
|
||||||
|
bool is_default = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListMicrophoneDevicesCommand {
|
||||||
|
message Request {}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
repeated MicrophoneInputDevice devices = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
message GetMicStateCommand {
|
message GetMicStateCommand {
|
||||||
message Request {
|
message Request {
|
||||||
CommandHeader.Request header = 1;
|
CommandHeader.Request header = 1;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import "cmvr/api/microphone_command.proto";
|
|||||||
package cmvr.api;
|
package cmvr.api;
|
||||||
|
|
||||||
service MicPhoneService {
|
service MicPhoneService {
|
||||||
|
rpc ListDevices(ListMicrophoneDevicesCommand.Request) returns (ListMicrophoneDevicesCommand.Feedback);
|
||||||
rpc GetStatus(GetMicStateCommand.Request) returns (GetMicStateCommand.Feedback);
|
rpc GetStatus(GetMicStateCommand.Request) returns (GetMicStateCommand.Feedback);
|
||||||
rpc StartRecord(StartMicRecordingCommand.Request) returns (StartMicRecordingCommand.Feedback);
|
rpc StartRecord(StartMicRecordingCommand.Request) returns (StartMicRecordingCommand.Feedback);
|
||||||
rpc StopRecord(StopMicRecordingCommand.Request) returns (StopMicRecordingCommand.Feedback);
|
rpc StopRecord(StopMicRecordingCommand.Request) returns (StopMicRecordingCommand.Feedback);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user