diff --git a/src/Services/AudioWriterService.h b/src/Services/AudioWriterService.h index a79c4be..f0c93a8 100644 --- a/src/Services/AudioWriterService.h +++ b/src/Services/AudioWriterService.h @@ -75,11 +75,11 @@ namespace snoop if (this->m_writingThread.joinable()) { this->m_writingThread.join(); - } + } if (this->m_uploadThread.joinable()) { this->m_uploadThread.join(); - } + } } // -------- Public control API (called from DeviceControlService handlers) -------- @@ -135,9 +135,11 @@ namespace snoop spdlog::info("StopRecordingNow ignored: not recording"); return; } + auto stoppedAtMs = NowMs(); + // Force-close current segment right away, enqueue, and disable recording. this->m_oggWriter->StopWriting(); - this->MoveToUploadQueue(this->m_currentRecordFilePath); + this->MoveToUploadQueue(this->m_currentRecordFilePath,this->m_currentRecordStartedAt,stoppedAtMs); m_recordingEnabled = false; m_stopAfterCurrentSegment = false; spdlog::info("Recording stopped immediately (deep sleep)"); @@ -264,7 +266,8 @@ namespace snoop void WritingThread() { - // recording starts ONLY when StartRecording() is called + constexpr unsigned long long DEFAULT_SEGMENT_MS = 30'000; // 30 seconds + while (!m_isIntermission) { if (!m_recordingEnabled.load()) @@ -273,32 +276,52 @@ namespace snoop continue; } - // Start a fresh segment - auto now = std::chrono::system_clock::now(); + // --- Get segment duration from config --- + unsigned long long segDurationMs = this->m_configService->GetRecordingDuration(); + if (segDurationMs < 1000) // anything < 1s is basically useless here + { + spdlog::warn("RecordingDuration={} ms is too small, using default {} ms", + segDurationMs, DEFAULT_SEGMENT_MS); + segDurationMs = DEFAULT_SEGMENT_MS; + } + spdlog::info("Starting new segment with target duration={} ms", segDurationMs); + + // --- Mark start times (wall + monotonic) --- + auto wallStart = std::chrono::system_clock::now(); this->m_currentRecordStartedAt = - std::chrono::duration_cast(now.time_since_epoch()).count(); - this->m_currentRecordFilePath = this->m_destinationDirectoryPath + std::to_string(this->m_currentRecordStartedAt); + std::chrono::duration_cast(wallStart.time_since_epoch()).count(); + + this->m_currentRecordFilePath = + this->m_destinationDirectoryPath + std::to_string(this->m_currentRecordStartedAt); + this->m_oggWriter->StartWriting(this->m_currentRecordFilePath); spdlog::info("Recording segment started: {}", this->m_currentRecordFilePath); - // Write until duration elapses - const auto segDurationMs = this->m_configService->GetRecordingDuration(); + auto monoStart = std::chrono::steady_clock::now(); + const auto targetDuration = std::chrono::milliseconds(segDurationMs); + + // --- Wait until segment duration elapses --- while (!m_isIntermission && m_recordingEnabled.load()) { - now = std::chrono::system_clock::now(); - auto currentRecordDuration = - std::chrono::duration_cast(now.time_since_epoch()).count() - this->m_currentRecordStartedAt; - if (currentRecordDuration >= segDurationMs) - { + auto elapsed = std::chrono::steady_clock::now() - monoStart; + if (elapsed >= targetDuration) break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); } - // Close current segment and enqueue + auto wallStop = std::chrono::system_clock::now(); + auto stoppedAtMs = + std::chrono::duration_cast(wallStop.time_since_epoch()).count(); + + // --- Close and enqueue --- this->m_oggWriter->StopWriting(); - this->MoveToUploadQueue(this->m_currentRecordFilePath); - spdlog::info("Recording segment finished: {}", this->m_currentRecordFilePath); + this->MoveToUploadQueue(this->m_currentRecordFilePath, + this->m_currentRecordStartedAt, + stoppedAtMs); + spdlog::info("Recording segment finished: {} ({} ms)", + this->m_currentRecordFilePath, + stoppedAtMs - this->m_currentRecordStartedAt); // If graceful stop requested, stop after finishing this segment if (m_stopAfterCurrentSegment.load()) @@ -309,28 +332,58 @@ namespace snoop } } - // If exiting service while in a middle of a segment, ensure clean close + // Clean shutdown if we exit while still recording if (m_recordingEnabled.load()) { + auto wallStop = std::chrono::system_clock::now(); + auto stoppedAtMs = + std::chrono::duration_cast(wallStop.time_since_epoch()).count(); + this->m_oggWriter->StopWriting(); - this->MoveToUploadQueue(this->m_currentRecordFilePath); + this->MoveToUploadQueue(this->m_currentRecordFilePath, + this->m_currentRecordStartedAt, + stoppedAtMs); m_recordingEnabled = false; } } + // Helper: ms since epoch + static unsigned long long NowMs() + { + auto now = std::chrono::system_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); + } - void MoveToUploadQueue(const std::string &filePath) + void MoveToUploadQueue(const std::string &filePath, unsigned long long startedAt, unsigned long long stoppedAt) { spdlog::info("MoveToUploadQueue( {} )", filePath); std::lock_guard lock(this->m_fetchFilePathsMutex); - auto now = std::chrono::system_clock::now(); - auto recordStoppedAt = std::chrono::duration_cast(now.time_since_epoch()).count(); if (std::filesystem::exists(filePath)) { - auto fileName = std::filesystem::path(filePath).filename().string() + "-" + std::to_string(recordStoppedAt); + auto fileName = std::to_string(startedAt) + "-" + std::to_string(stoppedAt); std::filesystem::rename(filePath, m_queueDirectoryPath + fileName); } } + // Legacy helper for startup recovery + void MoveToUploadQueue(const std::string &filePath) + { + const auto stoppedAt = NowMs(); + unsigned long long startedAt = 0; + + auto base = std::filesystem::path(filePath).filename().string(); + try + { + startedAt = std::stoull(base); + } + catch (...) + { + // if filename is not pure number, fall back to stoppedAt + startedAt = stoppedAt; + } + + MoveToUploadQueue(filePath, startedAt, stoppedAt); + } + void UploadThread() { const auto baseUrl = this->m_configService->GetBaseUrl(); @@ -341,7 +394,7 @@ namespace snoop if (!std::filesystem::exists(ca)) { ca = "/etc/iot/keys/ca_chain.pem"; - } + } const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem"; while (!m_isIntermission) @@ -356,7 +409,7 @@ namespace snoop if (entry.is_regular_file()) { files.push_back(entry.path()); - } + } } } catch (const std::exception &e)