feat(microphone): apply selected capture device

This commit is contained in:
linbo 2026-08-25 15:00:37 +08:00
parent e0da0f1f80
commit 05ab64cf76
5 changed files with 86 additions and 7 deletions

View File

@ -34,7 +34,11 @@ namespace cmvr::device{
} }
return health; return health;
} }
virtual void startRecording(const std::string& outputFilePath) {} virtual bool selectInputDevice(const std::string& input_device) {
(void)input_device;
return false;
}
virtual bool startRecording(const std::string& outputFilePath) { return false; }
virtual void stopRecording() {} virtual void stopRecording() {}
virtual void pause() {} virtual void pause() {}
virtual void resume() {} virtual void resume() {}

View File

@ -33,7 +33,8 @@ namespace cmvr::device {
bool start() override; bool start() override;
bool stop() override; bool stop() override;
void getState(MicrophoneState &state) override; void getState(MicrophoneState &state) override;
void startRecording(const std::string& outputFilePath) override; bool selectInputDevice(const std::string& input_device) override;
bool startRecording(const std::string& outputFilePath) override;
void stopRecording() override; void stopRecording() override;
void pause() override; void pause() override;
void resume() override; void resume() override;

View File

@ -149,20 +149,46 @@ void ffmpegMicroPhone::getState(MicrophoneState& state)
state = state_; state = state_;
} }
void ffmpegMicroPhone::startRecording(const std::string& outputFilePath) bool ffmpegMicroPhone::selectInputDevice(const std::string& input_device)
{
if (input_device.empty()) {
return false;
}
std::lock_guard capture_lock(capture_mutex_);
lock_guard lock(mtx_);
if (input_device == input_device_) {
return true;
}
if (is_capturing_.load(std::memory_order_acquire) ||
state_.is_recording || stream_count_ > 0) {
state_.is_error = true;
state_.error_message = "cannot switch microphone input while capture is active";
return false;
}
input_device_ = input_device;
config_.set_input_device(input_device);
state_.is_error = false;
state_.error_message.clear();
CMVR_LOG(INFO) << "[ffmpegMicroPhone] Selected input device: " << input_device_;
return true;
}
bool ffmpegMicroPhone::startRecording(const std::string& outputFilePath)
{ {
std::lock_guard capture_lock(capture_mutex_); std::lock_guard capture_lock(capture_mutex_);
{ {
lock_guard lock(mtx_); lock_guard lock(mtx_);
if (state_.is_recording) { if (state_.is_recording) {
CMVR_LOG(ERROR) << "[ffmpegMicroPhone] (startRecording): microphone is already recording"; CMVR_LOG(ERROR) << "[ffmpegMicroPhone] (startRecording): microphone is already recording";
return; return false;
} }
if (stream_count_ > 0 || is_capturing_) { if (stream_count_ > 0 || is_capturing_) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "cannot start recording while microphone streaming is active"; state_.error_message = "cannot start recording while microphone streaming is active";
CMVR_LOG(ERROR) << "[ffmpegMicroPhone] " << state_.error_message; CMVR_LOG(ERROR) << "[ffmpegMicroPhone] " << state_.error_message;
return; return false;
} }
size_t dot_pos = outputFilePath.find_last_of('.'); size_t dot_pos = outputFilePath.find_last_of('.');
@ -174,7 +200,7 @@ void ffmpegMicroPhone::startRecording(const std::string& outputFilePath)
lock_guard lock(mtx_); lock_guard lock(mtx_);
state_.is_error = true; state_.is_error = true;
state_.error_message = "FFmpeg init error"; state_.error_message = "FFmpeg init error";
return; return false;
} }
{ {
@ -183,6 +209,7 @@ void ffmpegMicroPhone::startRecording(const std::string& outputFilePath)
state_.is_error = false; state_.is_error = false;
state_.error_message.clear(); state_.error_message.clear();
} }
return true;
} }
void ffmpegMicroPhone::stopRecording() void ffmpegMicroPhone::stopRecording()

View File

@ -25,6 +25,29 @@ grpc::Status failResponse(ResponseT* response, const std::string& message) {
return grpc::Status::OK; return grpc::Status::OK;
} }
std::string selectInputDevice(
const std::shared_ptr<AbstractMicrophone>& microphone,
const std::string& input_device) {
if (input_device.empty()) {
return "Microphone input_device is required";
}
const auto available_devices = listAvailableMicrophoneInputDevices();
const auto selected = std::find_if(
available_devices.begin(),
available_devices.end(),
[&input_device](const MicrophoneInputDeviceInfo& device) {
return device.input_device == input_device;
});
if (selected == available_devices.end()) {
return "Microphone input device is unavailable: " + input_device;
}
if (!microphone->selectInputDevice(input_device)) {
return "Cannot select microphone input device while capture is active: " + input_device;
}
return {};
}
} }
gRPCMicroPhoneServiceImpl::gRPCMicroPhoneServiceImpl(): dmgr_(DeviceManager::getInstance()) {} gRPCMicroPhoneServiceImpl::gRPCMicroPhoneServiceImpl(): dmgr_(DeviceManager::getInstance()) {}
@ -96,10 +119,22 @@ grpc::Status gRPCMicroPhoneServiceImpl::StartRecord(grpc::ServerContext* context
if (!dev) { if (!dev) {
return failResponse(response, "Microphone device not found: " + dev_id); return failResponse(response, "Microphone device not found: " + dev_id);
} }
const std::string selection_error = selectInputDevice(dev, request->input_device());
if (!selection_error.empty()) {
return failResponse(response, selection_error);
}
if (!dev->start()) { if (!dev->start()) {
return failResponse(response, "Failed to start microphone: " + dev_id); return failResponse(response, "Failed to start microphone: " + dev_id);
} }
dev->startRecording(request->file_path()); if (!dev->startRecording(request->file_path())) {
MicrophoneState state;
dev->getState(state);
return failResponse(
response,
state.error_message.empty()
? "Failed to start microphone recording: " + dev_id
: state.error_message);
}
response->mutable_header()->set_success(true); response->mutable_header()->set_success(true);
setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); setCurrentTimestamp(response->mutable_header()->mutable_timestamp());
CMVR_LOG(DEBUG) << "[gRPCMicroPhoneServiceImpl] (StartRecord): success, id=" << dev_id CMVR_LOG(DEBUG) << "[gRPCMicroPhoneServiceImpl] (StartRecord): success, id=" << dev_id
@ -199,6 +234,16 @@ grpc::Status gRPCMicroPhoneServiceImpl::StreamAudio(grpc::ServerContext* context
return grpc::Status::OK; return grpc::Status::OK;
} }
const std::string selection_error = selectInputDevice(dev, request->input_device());
if (!selection_error.empty()) {
api::StreamMicAudioCommand_Feedback feedback;
feedback.mutable_header()->set_success(false);
feedback.mutable_header()->set_error_message(selection_error);
setCurrentTimestamp(feedback.mutable_header()->mutable_timestamp());
writer->Write(feedback);
return grpc::Status::OK;
}
auto& media_hub = cmvr::media::globalMediaSourceHub(); auto& media_hub = cmvr::media::globalMediaSourceHub();
const std::string track_id = cmvr::media::microphoneTrackId(dev_id); const std::string track_id = cmvr::media::microphoneTrackId(dev_id);
if (!cmvr::media::ensureMicrophoneMediaSource(media_hub, dev)) { if (!cmvr::media::ensureMicrophoneMediaSource(media_hub, dev)) {

View File

@ -43,6 +43,7 @@ message StartMicRecordingCommand {
message Request { message Request {
CommandHeader.Request header = 1; CommandHeader.Request header = 1;
string file_path = 2; string file_path = 2;
string input_device = 3;
} }
message Feedback { message Feedback {
CommandHeader.Feedback header = 1; CommandHeader.Feedback header = 1;
@ -80,6 +81,7 @@ message ResumeMicRecordingCommand {
message StreamMicAudioCommand { message StreamMicAudioCommand {
message Request { message Request {
CommandHeader.Request header = 1; CommandHeader.Request header = 1;
string input_device = 2;
} }
message Feedback { message Feedback {
CommandHeader.Feedback header = 1; CommandHeader.Feedback header = 1;