diff --git a/cmvr-es/devices/microphone/abstract_microphone.h b/cmvr-es/devices/microphone/abstract_microphone.h index b4770343..f7f7798c 100644 --- a/cmvr-es/devices/microphone/abstract_microphone.h +++ b/cmvr-es/devices/microphone/abstract_microphone.h @@ -34,7 +34,11 @@ namespace cmvr::device{ } 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 pause() {} virtual void resume() {} diff --git a/cmvr-es/devices/microphone/ffmpeg_microphone/include/ffmpeg_microphone.h b/cmvr-es/devices/microphone/ffmpeg_microphone/include/ffmpeg_microphone.h index cd41d881..c9c2fb31 100644 --- a/cmvr-es/devices/microphone/ffmpeg_microphone/include/ffmpeg_microphone.h +++ b/cmvr-es/devices/microphone/ffmpeg_microphone/include/ffmpeg_microphone.h @@ -33,7 +33,8 @@ namespace cmvr::device { bool start() override; bool stop() 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 pause() override; void resume() override; diff --git a/cmvr-es/devices/microphone/ffmpeg_microphone/src/ffmpeg_microphone.cpp b/cmvr-es/devices/microphone/ffmpeg_microphone/src/ffmpeg_microphone.cpp index 9e254c52..197fc365 100644 --- a/cmvr-es/devices/microphone/ffmpeg_microphone/src/ffmpeg_microphone.cpp +++ b/cmvr-es/devices/microphone/ffmpeg_microphone/src/ffmpeg_microphone.cpp @@ -149,20 +149,46 @@ void ffmpegMicroPhone::getState(MicrophoneState& 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_); { lock_guard lock(mtx_); if (state_.is_recording) { CMVR_LOG(ERROR) << "[ffmpegMicroPhone] (startRecording): microphone is already recording"; - return; + return false; } if (stream_count_ > 0 || is_capturing_) { state_.is_error = true; state_.error_message = "cannot start recording while microphone streaming is active"; CMVR_LOG(ERROR) << "[ffmpegMicroPhone] " << state_.error_message; - return; + return false; } size_t dot_pos = outputFilePath.find_last_of('.'); @@ -174,7 +200,7 @@ void ffmpegMicroPhone::startRecording(const std::string& outputFilePath) lock_guard lock(mtx_); state_.is_error = true; 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_.error_message.clear(); } + return true; } void ffmpegMicroPhone::stopRecording() diff --git a/cmvr-es/service/grpc/src/grpc_microphone_service.cpp b/cmvr-es/service/grpc/src/grpc_microphone_service.cpp index 5f6f00a9..d75bfd18 100644 --- a/cmvr-es/service/grpc/src/grpc_microphone_service.cpp +++ b/cmvr-es/service/grpc/src/grpc_microphone_service.cpp @@ -25,6 +25,29 @@ grpc::Status failResponse(ResponseT* response, const std::string& message) { return grpc::Status::OK; } +std::string selectInputDevice( + const std::shared_ptr& 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()) {} @@ -96,10 +119,22 @@ grpc::Status gRPCMicroPhoneServiceImpl::StartRecord(grpc::ServerContext* context if (!dev) { 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()) { 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); setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); CMVR_LOG(DEBUG) << "[gRPCMicroPhoneServiceImpl] (StartRecord): success, id=" << dev_id @@ -199,6 +234,16 @@ grpc::Status gRPCMicroPhoneServiceImpl::StreamAudio(grpc::ServerContext* context 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(); const std::string track_id = cmvr::media::microphoneTrackId(dev_id); if (!cmvr::media::ensureMicrophoneMediaSource(media_hub, dev)) { diff --git a/protos/cmvr/api/microphone_command.proto b/protos/cmvr/api/microphone_command.proto index add491d9..20d2b100 100644 --- a/protos/cmvr/api/microphone_command.proto +++ b/protos/cmvr/api/microphone_command.proto @@ -43,6 +43,7 @@ message StartMicRecordingCommand { message Request { CommandHeader.Request header = 1; string file_path = 2; + string input_device = 3; } message Feedback { CommandHeader.Feedback header = 1; @@ -80,6 +81,7 @@ message ResumeMicRecordingCommand { message StreamMicAudioCommand { message Request { CommandHeader.Request header = 1; + string input_device = 2; } message Feedback { CommandHeader.Feedback header = 1;