동영상 파일을 cv::VideoCapture
로 읽으면 헤더정보를 읽어올 수 가 있는데,
이 헤더정보가 실제 데이터와 다른 경우가 종종 존재한다. -_-;
동영상을 열기 전까지 헤더가 올바른지 아닌지 판별하기 어려우니 아래의 방법을 추천한다.
int64_t GetVideoFrameCount(std::string video_file) {
cv::VideoCapture vc(video_file);
cv::Mat frame;
int64_t pos = 0;
while (true) {
vc.grab();
if ((int64_t)vc.get(CV_CAP_PROP_POS_FRAMES) - pos == 0) {
break;
}
++pos;
}
return pos;
}
cv::VideoCapture::grab
함수는 다음 프레임으로 이동하는것이다.
같이 사용하는 함수로 cv::VideoCapture::retrieve(cv::OutputArray image,int flag=0)
함수가 있다.
retrieve 함수는 해당 프레임을 cv::Mat
으로 가져온다.
즉!, cv::VideoCapture::read
나 cv::VideoCapture::operator>>
는 저 두 함수를 합친것이다.
실제 읽을수있는 프레임을 측정하는데 있어서, 데이터를 읽을 필요는 없다.
실제로 grab 만 사용한경우가 몇십배는 빠르다.
심지어 몇백배는 빠르다.
아래에서 설명한다.
영상의 길이는 OPENCV 에서는 제공하고 있지 않다.(내가 모르는걸수도)
다시 말하지만 헤더정보를 이용해 duration을 구할수 있는데, 헤더가 잘못된걸 수도 있다.
그래서 ffmpeg 를 사용한다.
int64_t GetVideoDuration(std::string video_file) { // -> msec
av_register_all();
AVFormatContext* pFormatCtx = nullptr;
avformat_open_input(&pFormatCtx, video_file.c_str(), NULL, NULL);
int64_t duration = pFormatCtx->duration*1000 / AV_TIME_BASE;
return duration;
}
근데 어떤 영상에서는 duration 정보가 헤더에 없는 경우가 있다.
이럴때는 avformat_find_stream_info
로 모든 정보를 다 찾을 수 있다.
이제 모든 정보를 한번에 빠르게 구해보자.
#include<iostream>
extern "C" {
#include <libavformat/avformat.h>
#include<libavcodec/avcodec.h>
}
void GetVideoInfo(std::string video_file, int64_t* pframe_count = nullptr, int64_t* pduration_msec = nullptr, double* pfps = nullptr) {
bool ret = true;
av_register_all();
AVFormatContext* pFormatCtx = nullptr;
avformat_open_input(&pFormatCtx, video_file.c_str(), NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);
int videoStream = -1; //Find video Stream
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}
int64_t frame_count = pFormatCtx->streams[videoStream]->nb_frames;
if (frame_count<=0 || frame_count>INT_MAX) {
//frame_count is incorrect
frame_count = 0;
AVPacket packet;
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
frame_count++;
}
av_packet_unref(&packet);
}
}
int64_t duration_msec = pFormatCtx->duration*1000.0 / AV_TIME_BASE;
double fps = frame_count * 1000.0 / duration_msec;
if (frame_count) {
*pframe_count = frame_count;
}
if (pduration_msec) {
*pduration_msec = duration_msec;
}
if (pfps) {
*pfps = fps;
}
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
}
int main() {
double start = clock();
std::string video_file = "OBGB_20170809_13293(2).avi";
int64_t duration, frame_count;
double fps;
GetVideoInfo(video_file, &frame_count, &duration, &fps);
std::cout << frame_count << std::endl;
std::cout << duration << std::endl;
std::cout << fps << std::endl;
std::cout << (clock() - start) / CLOCKS_PER_SEC << std::endl;
}