반응형

1개 이상의 동영상 파일을 로드하고 재생해 보자.

 

아래 코드는 각각의 영상을 독립적으로 처리하기 때문에 시간 표시도 개별적으로 되고 앞뒤 이동도 제한된다.

 

#include <iostream>
#include <vector>
#include <filesystem>
#include <opencv2/opencv.hpp>

int main()
{
	std::vector<std::string> videoFiles;
	std::string folderPath = "./";

	for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(folderPath))
	{
		if (entry.is_regular_file() && entry.path().extension() == ".mkv")
		{
			videoFiles.push_back(entry.path().string());
		}
	}

	std::cout << "Found " << videoFiles.size() << " .mkv files in the folder:" << std::endl;
	for (const std::string& file : videoFiles)
	{
		std::cout << file << std::endl;
	}

	std::vector<cv::VideoCapture> videoCaptures;
	for (const std::string& file : videoFiles)
	{
		cv::VideoCapture cap(file);
		if (!cap.isOpened())
		{
			std::cerr << "Error opening video file: " << file << std::endl;
			continue;
		}
		videoCaptures.push_back(std::move(cap)); // Move the VideoCapture object into the vector
	}

	std::cout << "Successfully opened " << videoCaptures.size() << " video files." << std::endl;

	cv::Mat frame;
	double timestamp = 0.0;
	bool quit = false;
	std::string time;
	int key = 0;

	for (size_t i = 0; i < videoCaptures.size(); ++i)
	{
		std::cout << "Playing video: " << videoFiles[i] << std::endl;
		videoCaptures[i] >> frame;

		while (!frame.empty())
		{
			timestamp = videoCaptures[i].get(cv::CAP_PROP_POS_MSEC); // Get the current timestamp
			time = std::to_string(timestamp / 1000.0);
			time = time.substr(0, time.find(".") + 3); // Keep only 2 decimal places

			cv::putText(frame, time, cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2);
			cv::imshow("Video", frame);

			key = cv::waitKey(33);
			if (key == 27)
			{
				quit = true;
				break;
			}
			else if (key == 'f')
				// 5초 앞으로
				videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, timestamp + 5.0 * 1000.0);
			else if (key == 'b')
				 // 5초 뒤로
				videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, timestamp - 5.0 * 1000.0);

			videoCaptures[i] >> frame;
		}

		if (quit)
			break;
	}

	for (cv::VideoCapture& cap : videoCaptures)
		cap.release();

	cv::destroyAllWindows();

	return 0;
}

 

f: 5초 앞으로

b: 5초 뒤로

 

 

아래 코드는 각각의 영상을 하나의 영상인 것처럼 시간을 표시하고 5초 앞뒤로 이동할 때도 각 영상을 자연스럽게 이동한다.

 

#include <iostream>
#include <vector>
#include <filesystem>
#include <opencv2/opencv.hpp>

int main()
{
	std::vector<std::string> videoFiles;
	std::string folderPath = "./";

	for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(folderPath))
	{
		if (entry.is_regular_file() && entry.path().extension() == ".mkv")
		{
			videoFiles.push_back(entry.path().string());
		}
	}

	std::cout << "Found " << videoFiles.size() << " .mkv files in the folder:" << std::endl;
	for (const std::string& file : videoFiles)
	{
		std::cout << file << std::endl;
	}

	std::vector<cv::VideoCapture> videoCaptures;
	for (const std::string& file : videoFiles)
	{
		cv::VideoCapture cap(file);
		if (!cap.isOpened())
		{
			std::cerr << "Error opening video file: " << file << std::endl;
			continue;
		}
		videoCaptures.push_back(std::move(cap)); // Move the VideoCapture object into the vector
	}

	std::cout << "Successfully opened " << videoCaptures.size() << " video files." << std::endl;

	cv::Mat frame;
	double timestamp = 0.0;
	bool quit = false;
	std::string time;
	double duration = 0.0;
	double move = 5000.0; // 5 seconds in milliseconds
	int key = 0;

	for (size_t i = 0; i < videoCaptures.size(); ++i)
	{
		std::cout << "Playing video: " << videoFiles[i] << std::endl;
		videoCaptures[i] >> frame;

		duration = 0.0;
		for (int j = 0; j < i; j++) {
			duration += (videoCaptures[j].get(cv::CAP_PROP_FRAME_COUNT) / videoCaptures[j].get(cv::CAP_PROP_FPS));
		}
		duration *= 1000.0; // Convert to milliseconds

		while (!frame.empty())
		{
			timestamp = duration + videoCaptures[i].get(cv::CAP_PROP_POS_MSEC); // Get the current timestamp
			time = std::to_string(timestamp / 1000.0);
			time = time.substr(0, time.find(".") + 3); // Keep only 2 decimal places

			cv::putText(frame, time, cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2);
			cv::imshow("Video", frame);

			key = cv::waitKey(33);
			if (key == 27)
			{
				quit = true;
				break;
			}
			else if (key == 'f')
			{
				// 현재 영상에서 5초 앞으로 가는 것이 가능한 경우, 5초 앞으로 이동
				if (videoCaptures[i].get(cv::CAP_PROP_POS_MSEC) + move < videoCaptures[i].get(cv::CAP_PROP_FRAME_COUNT) / videoCaptures[i].get(cv::CAP_PROP_FPS) * 1000.0)
					videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, videoCaptures[i].get(cv::CAP_PROP_POS_MSEC) + move);
				// 현재 영상에서 5초 앞으로 가는 것이 불가능한 경우, 가능한 만큼만 이동하고 다음 영상으로 이동해서 남은 시간만큼 앞으로 이동
				else if (i < videoCaptures.size() - 1)
				{
					videoCaptures[i + 1].set(cv::CAP_PROP_POS_MSEC, move - (videoCaptures[i].get(cv::CAP_PROP_FRAME_COUNT) / videoCaptures[i].get(cv::CAP_PROP_FPS) * 1000.0 - videoCaptures[i].get(cv::CAP_PROP_POS_MSEC)));

					// 현재 영상(i)을 나중에 다시 재생할 때 처음부터 재생될 수 있도록 위치를 0으로 초기화하는건 불필요.
					//videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, 0.0);

					break;
				}
				else
				{
					//videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, videoCaptures[i].get(cv::CAP_PROP_FRAME_COUNT) / videoCaptures[i].get(cv::CAP_PROP_FPS) * 1000.0);
				}

			}
			else if (key == 'b')
			{
				// 현재 영상에서 5초 뒤로 가는 것이 가능한 경우, 5초 뒤로 이동
				if (videoCaptures[i].get(cv::CAP_PROP_POS_MSEC) > move)
					videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, videoCaptures[i].get(cv::CAP_PROP_POS_MSEC) - move);
				// 현재 영상에서 5초 뒤로 가는 것이 불가능한 경우, 가능한 만큼만 이동하고 이전 영상으로 이동해서 남은 시간만큼 뒤로 이동
				else if (i > 0)
				{
					videoCaptures[i - 1].set(cv::CAP_PROP_POS_MSEC, videoCaptures[i - 1].get(cv::CAP_PROP_FRAME_COUNT) / videoCaptures[i - 1].get(cv::CAP_PROP_FPS) * 1000.0 - (move - videoCaptures[i].get(cv::CAP_PROP_POS_MSEC)));

					// 현재 영상(i)을 곧(5초 이내) 다시 재생할 때 처음부터 재생될 수 있도록 위치를 0으로 초기화.
					videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, 0.0);

					i -= 2; // 다음 루프에서 i++ 되므로 -2

					break;
				}
				else
				{
					videoCaptures[i].set(cv::CAP_PROP_POS_MSEC, 0.0);
				}
			}

			videoCaptures[i] >> frame;
		}

		if (quit)
			break;
	}

	for (cv::VideoCapture& cap : videoCaptures)
		cap.release();

	cv::destroyAllWindows();

	return 0;
}

 

반응형
Posted by J-sean
: