Как создать точные таймеры и циклы на C++ для захвата RTSP.

Вопрос или проблема

Я пытаюсь захватить поток и записать его на куски по 30 секунд. Я не знаю, какова будет частота кадров у камер, поэтому мне нужно учитывать, что я хочу захватывать только 15 fps.

Моя проблема в том, что следующий код генерирует куски в 28 секунд вместо 30. Как мне правильно настроить тайминг?

Я не новичок в C++, но немного зашел в ржавчину и никогда не делал ничего подобного раньше (обычно это простые ввод/вывод или в контексте разработки простых игр).

Должно быть, что-то не так с математикой или я делаю что-то, что вызывает задержку в обработке, или просто слишком долго смотрю на это и пропускаю что-то очевидное (вероятно).

Вот что я пробовал до сих пор:

int frame_count = 0;
double frame_duration = 1.0 / currentCameraConfig.fps;
auto start_time = std::chrono::steady_clock::now();

while (true) {
    cv::Mat frame;

    if (!cap.read(frame)) {
        break;
    }

    frame_count++;

    auto now = std::chrono::steady_clock::now();
    auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count();

    if (elapsed_time < 30) {
        video_output.write(frame);
        std::cout << "[INFO]: elapsed time " << elapsed_time << std::endl;
        std::cout << "[INFO]: Saving video frame " << frame_count << " at " << currentCameraConfig.fps << " fps....cam " << std::endl;
    }

    if (cv::waitKey(1) == 'q' || elapsed_time >= 30) {
        std::cout << "\n [INFO]: Stopping recording..." << std::endl;
        break;
    }

    auto frame_end_time = std::chrono::steady_clock::now();
    auto frame_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(frame_end_time - now).count();

    int sleep_time = static_cast<int>((frame_duration * 1000) - frame_elapsed);
    if (sleep_time > 0) {
        std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
    }
}

cap.release();
video_output.release();
std::cout << "Video saved successfully.!" << std::endl;

Ответ или решение

Чтобы создать точный таймер в цикле записи потокового видео RTSP и разбивать его на 30-секундные фрагменты при частоте кадров в 15 FPS, нужно учитывать различные факторы, которые могут вызвать задержки и некорректное время захвата. Я уточню ваш код и предложу оптимизации, чтобы достичь желаемого поведения.

Оптимизированный код

#include <iostream>
#include <chrono>
#include <thread>
#include <opencv2/opencv.hpp>

void capture_stream(cv::VideoCapture& cap, cv::VideoWriter& video_output, double target_fps) {
    int frame_count = 0;
    double target_frame_duration = 1.0 / target_fps; // полное время фрейма (в секундах)
    auto start_time = std::chrono::steady_clock::now();

    while (true) {
        cv::Mat frame;

        if (!cap.read(frame)) {
            std::cerr << "[ERROR]: Не удалось прочитать кадр." << std::endl;
            break;
        }

        frame_count++;
        video_output.write(frame);

        auto now = std::chrono::steady_clock::now();
        auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count();

        std::cout << "[INFO]: elasped time: " << elapsed_time << " seconds" << std::endl;
        std::cout << "[INFO]: Saving video frame " << frame_count << " at " << target_fps << " fps." << std::endl;

        // Проверка времени записи
        if (elapsed_time >= 30) {
            std::cout << "[INFO]: Достигнуто время записи. Остановка..." << std::endl;
            break;
        }

        // Подсчет времени прошедшего с начала обработки кадра
        auto frame_start_time = std::chrono::steady_clock::now();
        auto frame_elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(frame_start_time - now).count();

        // Расчет времени ожидания до следующего кадра
        int sleep_time = static_cast<int>((target_frame_duration * 1000) - frame_elapsed_time);
        if (sleep_time > 0) {
            std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
        }
    }

    cap.release();
    video_output.release();
    std::cout << "[INFO]: Видео успешно сохранено!" << std::endl;
}

int main() {
    cv::VideoCapture cap("rtsp://your_camera_stream");
    if (!cap.isOpened()) {
        std::cerr << "[ERROR]: Не удалось открыть поток." << std::endl;
        return -1;
    }

    cv::VideoWriter video_output("output.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 15, cv::Size(640, 480)); // Убедитесь в правильных параметрах вывода
    capture_stream(cap, video_output, 15.0);

    return 0;
}

Объяснение изменений и оптимизаций:

  1. Параллельная обработка времени: Я добавил переменную frame_start_time, чтобы точно отслеживать, сколько времени уходит на обработку каждого кадра. Это позволяет более точно вычислить, сколько времени нам еще нужно подождать до следующего кадра.

  2. Запись видео: Запись фреймов выполняется вне временной проверки, чтобы удостовериться, что мы записываем все захваченные кадры до достижения 30 секунд.

  3. Устранение задержек: Убедитесь, что задержка между кадрами учитывает фактическое время обработки, таким образом мы минимизируем неоптимальные временные задержки.

  4. Проверка ошибок: Я добавил базовую обработку ошибок для проверки, открывается ли поток и считываются ли кадры.

  5. Запись до полной 30-секундной отметки: Мы продолжаем записывать видео до достижения отметки в 30 секунд, а не останавливаем обработку при превышении 30 секунд.

Следуя описанным рекомендациям, вы сможете получить более точное управление временем записи и избежать неожиданной потери кадров.

Оцените материал
Добавить комментарий

Капча загружается...