最近遇到Opencv Videocapture的项目,记录一下:
#include <iostream>
// #ifdef __cplusplus
// extern "C" {
// #endif
// #ifdef __cplusplus
// }
// #endif
#include "opencv2/core/cvdef.h"
#include "opencv2/core/utils/logger.hpp"
#include "opencv2/opencv.hpp"
using namespace cv;
#define CV_FOURCC(c1, c2, c3, c4) \
(((c1)&255) + (((c2)&255) << 8) + (((c3)&255) << 16) + (((c4)&255) << 24))
int main() {
cv::utils::logging::setLogLevel(utils::logging::LOG_LEVEL_DEBUG);
VideoCapture capture;
Mat frame;
// char *infile = "/workspace/videos/codec-videos/zzsin_1920x1080_30fps_60s.mp4";
char *infile = "/workspace/videos/codec-videos/image_all/ocr_1600-1600.mp4";
if (!capture.open(infile, CAP_FFMPEG)) {
printf("can not open infile %s ...\n", infile);
return -1;
}
cv::Size sWH = cv::Size(
(int)capture.get(CAP_PROP_FRAME_WIDTH), (int)capture.get(CAP_PROP_FRAME_HEIGHT));
char * outfile = "./test.mp4";
VideoWriter outputVideo;
if (!outputVideo.open(outfile, CAP_FFMPEG, CV_FOURCC('a', 'v', 'c', '1'), 25.0, sWH)) {
printf("can not open outfile %s ...\n", outfile);
return -1;
}
while (capture.read(frame)) {
if (frame.empty())
break;
outputVideo << frame;
}
printf("end of stream!\n");
capture.release();
outputVideo.release();
return 0;
}
下面主要看一下capture.open(infile, CAP_FFMPEG)的流程,是如何调用到ffmpeg的:
bool VideoCapture::open(const String& filename, int apiPreference)
{
CV_TRACE_FUNCTION();
if (isOpened()) release();
icap = IVideoCapture_create(filename, apiPreference);
if (!icap.empty())
return true;
//跳转到这里来
cap.reset(cvCreateFileCaptureWithPreference(filename.c_str(), apiPreference));
return isOpened();
}
下面这句非常重要,apiPreference用户不能设置
CV_IMPL CvCapture * cvCreateFileCaptureWithPreference (const char * filename, int apiPreference)
{
CvCapture * result = 0;
switch(apiPreference) {
//注意这句话
default:
// user specified an API we do not know
// bail out to let the user know that it is not available
if (apiPreference) break;
#ifdef HAVE_FFMPEG
case CAP_FFMPEG:
TRY_OPEN(result, cvCreateFileCapture_FFMPEG_proxy (filename))
if (apiPreference) break;
#endif
...
}
CvCapture* cvCreateFileCapture_FFMPEG_proxy(const char * filename)
{
CvCapture_FFMPEG_proxy* result = new CvCapture_FFMPEG_proxy;
if( result->open( filename ))
return result;
delete result;
return 0;
}
bool CvCapture_FFMPEG::open(const char *_filename) {
CV_LOG_DEBUG("===CvCapture_FFMPEG==open()");
AutoLock lock(_mutex);
unsigned i;
bool valid = false;
close();
#if USE_AV_INTERRUPT_CALLBACK
/* interrupt callback */
interrupt_metadata.timeout_after_ms = LIBAVFORMAT_INTERRUPT_OPEN_TIMEOUT_MS;
get_monotonic_time(&interrupt_metadata.value);
ic = avformat_alloc_context();
ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback;
ic->interrupt_callback.opaque = &interrupt_metadata;
#endif
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
#ifndef NO_GETENV
char *options = getenv("OPENCV_FFMPEG_CAPTURE_OPTIONS");
if (options == NULL) {
av_dict_set(&dict, "rtsp_transport", "tcp", 0);
} else {
#if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 ? CALC_FFMPEG_VERSION(52, 17, 100) \
: CALC_FFMPEG_VERSION(52, 7, 0))
av_dict_parse_string(&dict, options, ";", "|", 0);
#else
av_dict_set(&dict, "rtsp_transport", "tcp", 0);
#endif
}
#else
av_dict_set(&dict, "rtsp_transport", "tcp", 0);
#endif
int err = avformat_open_input(&ic, _filename, NULL, &dict);
#else
int err = av_open_input_file(&ic, _filename, NULL, 0, NULL);
#endif
if (err < 0) {
CV_WARN("Error opening file");
CV_WARN(_filename);
goto exit_func;
}
err =
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 6, 0)
avformat_find_stream_info(ic, NULL);
#else
av_find_stream_info(ic);
#endif
if (err < 0) {
CV_WARN("Could not find codec parameters");
goto exit_func;
}
for (i = 0; i < ic->nb_streams; i++) {
#if LIBAVFORMAT_BUILD > 4628
AVCodecContext *enc = ic->streams[i]->codec;
#else
AVCodecContext *enc = &ic->streams[i]->codec;
#endif
//#ifdef FF_API_THREAD_INIT
// avcodec_thread_init(enc, get_number_of_cpus());
//#else
enc->thread_count = get_number_of_cpus();
//#endif
#if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(53, 2, 0)
#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
#endif
if (AVMEDIA_TYPE_VIDEO == enc->codec_type && video_stream < 0) {
// backup encoder' width/height
int enc_width = enc->width;
int enc_height = enc->height;
AVCodec *codec = avcodec_find_decoder(enc->codec_id);
if (!codec ||
#if LIBAVCODEC_VERSION_INT >= ((53 << 16) + (8 << 8) + 0)
avcodec_open2(enc, codec, NULL)
#else
avcodec_open(enc, codec)
#endif
< 0)
goto exit_func;
// checking width/height (since decoder can sometimes alter it, eg. vp6f)
if (enc_width && (enc->width != enc_width)) {
enc->width = enc_width;
}
if (enc_height && (enc->height != enc_height)) {
enc->height = enc_height;
}
video_stream = i;
video_st = ic->streams[i];
#if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 ? CALC_FFMPEG_VERSION(55, 45, 101) \
: CALC_FFMPEG_VERSION(55, 28, 1))
picture = av_frame_alloc();
#else
picture = avcodec_alloc_frame();
#endif
frame.width = enc->width;
frame.height = enc->height;
frame.cn = 3;
frame.step = 0;
frame.data = NULL;
break;
}
}
if (video_stream >= 0)
valid = true;
exit_func:
#if USE_AV_INTERRUPT_CALLBACK
// deactivate interrupt callback
interrupt_metadata.timeout_after_ms = 0;
#endif
if (!valid)
close();
return valid;
}