当前位置: 首页 > 工具软件 > VideoCapture > 使用案例 >

OpenCV3.4之VideoCapture分析

柯宜年
2023-12-01

最近遇到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;
}
 类似资料: