#pragma once #include #include #include #include #include #include #include #include namespace cc { inline double video_duration_seconds(const std::string& path) { cv::VideoCapture cap(path); if (!cap.isOpened()) throw std::runtime_error("Unable to open video for duration probe: " + path); double fc = cap.get(cv::CAP_PROP_FRAME_COUNT); double fps = cap.get(cv::CAP_PROP_FPS); cap.release(); if (fps > 0 && fc > 0) return fc / fps; throw std::runtime_error("Unable to determine duration for video: " + path); } inline double file_size_mb(const std::string& path) { return static_cast(std::filesystem::file_size(path)) / (1024.0 * 1024.0); } inline void run_ffmpeg(const std::vector& command) { std::string cmd; for (const auto& arg : command) cmd += arg + " "; cmd = cmd.substr(0, cmd.size() - 1) + " 2>&1"; int ret = std::system(cmd.c_str()); if (ret != 0) throw std::runtime_error("ffmpeg failed with code " + std::to_string(ret)); } inline double compress_video_to_target( const std::string& input_path, const std::string& output_path, int max_mb = 200, int max_attempts = 3) { namespace fs = std::filesystem; if (!fs::is_regular_file(input_path)) throw std::runtime_error("Input video not found: " + input_path); fs::create_directories(fs::path(output_path).parent_path()); double duration = video_duration_seconds(input_path); if (duration <= 0) throw std::runtime_error("Invalid video duration for " + input_path); int target_kbps = std::max(300, static_cast((max_mb * 8192) / duration * 0.92)); for (int attempt = 0; attempt < max_attempts; ++attempt) { int attempt_kbps = std::max(300, static_cast(target_kbps * std::pow(0.85, attempt))); if (fs::exists(output_path)) fs::remove(output_path); std::vector> codec_attempts = { {"-c:v", "h264_nvmpi", "-b:v", std::to_string(attempt_kbps) + "k", "-maxrate", std::to_string(attempt_kbps) + "k", "-bufsize", std::to_string(attempt_kbps * 2) + "k"}, {"-c:v", "libx264", "-preset", "fast", "-b:v", std::to_string(attempt_kbps) + "k", "-maxrate", std::to_string(attempt_kbps) + "k", "-bufsize", std::to_string(attempt_kbps * 2) + "k"} }; bool succeeded = false; for (const auto& cargs : codec_attempts) { std::vector cmd = {"ffmpeg", "-y", "-i", input_path}; cmd.insert(cmd.end(), cargs.begin(), cargs.end()); cmd.push_back("-c:a"); cmd.push_back("copy"); cmd.push_back(output_path); try { run_ffmpeg(cmd); succeeded = true; break; } catch (const std::runtime_error&) { if (fs::exists(output_path)) fs::remove(output_path); } } if (!succeeded) throw std::runtime_error("Unable to compress video: " + input_path); double size_mb = file_size_mb(output_path); std::cerr << "[compress] " << fs::path(output_path).filename().string() << ": " << size_mb << " MB (attempt " << (attempt + 1) << ", target " << attempt_kbps << " kbps)\n"; if (size_mb <= max_mb) return size_mb; } double final_size = file_size_mb(output_path); if (final_size > max_mb) throw std::runtime_error("Compressed video exceeds " + std::to_string(max_mb) + " MB: " + output_path); return final_size; } } // namespace cc