【问题标题】:Using OpenCV 2.4.4 with FFmpeg in Windows在 Windows 中使用 OpenCV 2.4.4 和 FFmpeg
【发布时间】:2013-03-26 19:52:13
【问题描述】:

我知道还有其他关于 OpenCV 中 FFmpeg 使用的问题,但其中大多数似乎已经过时了。

通过在 CMake 中打开 makefile,我可以验证我是否打开了 WITH_FFMPEG 标志。我的 OpenCV 构建输出文件夹包含一个 bin 文件夹,其中有 DebugRelease 文件夹,每个文件夹都包含一个名为 opencv_ffmpeg244.dll 的 .dll 文件的副本。当我创建 VideoWriter 并验证指向 .dll 的函数指针是否正确填充时,我可以进入 OpenCV 的源代码。这似乎很有效。

如果我使用 CV_FOURCC_PROMPT 的 FOURCC 代码,以下编解码器工作正常

  • 微软视频 1
  • 英特尔 IYUV 编解码器
  • 罗技视频 (I420)
  • Radius 的 Cinepak 编解码器
  • 全帧(未压缩)

以下编解码器无法正常工作(即生成 0kb 视频文件):

  • 微软 RLE

如果我的理解是正确的,使用 FFMPEG 应该允许使用一大堆新的编解码器(x264、DIVX、XVID 等)对视频进行编码。但是,这些都没有出现在提示中。使用宏 CV_FOURCC(...) 通过 FOURCC 代码手动设置它们也不起作用。例如,使用这个:CV_FOURCC('X','2','6','4') 会产生消息:

Could not find encoder for codec id 28: Encoder not found

并制作一个大小为 0kb 的视频文件。

使用这个:CV_FOURCC('X','V','I','D') 不会产生错误消息,并制作一个 6kb 的视频文件,在 Windows Media Player 或 VLC 中无法播放。

我尝试从 Xvid.org 手动下载 Xvid 编解码器。安装后,它出现在提示中的 VFW 选项下,并且编码正常工作。所以它接近解决方案,但如果我尝试直接设置FOURCC代码,它仍然像上面一样失败!我每次都必须从提示中选择它。 FFmpeg 不应该包含一大堆编解码器吗?如果是这样,为什么我要手动下载编解码器而不是使用 FFmpeg 内置的编解码器?

我在这里缺少什么?有没有办法检查 FFMPEG 是否“启用”?提示中唯一可用的编解码器似乎是 VFW 编解码器,而不是 FFMPEG 编解码器。 .dll 已构建并与可执行文件位于同一文件夹中,但似乎没有以任何方式使用它。

这里有很多相关的问题。希望找到了解 OpenCV 中 FFmpeg 实现的人,并且对所有这些部分如何组合在一起有所了解。

【问题讨论】:

    标签: windows opencv ffmpeg codec


    【解决方案1】:

    如何分别运行 ffmpeg 和您的应用程序并使用管道数据传输图像?

    将视频导入opencv程序,

    ffmpeg -i input.mp4 -vcodec mjpeg -f image2pipe -pix_fmt yuvj420p -r 10 -|program.exe
    

    用于录音等

    program.exe|ffmpeg -r 10 -vcodec mjpeg -f image2pipe -i - -vcodec h264 output.mp4
    

    program.exe 应该能够从标准输入读取连接的 jpeg 图像并将其写入标准输出,上述工作流程将起作用。这是一些从标准输入读取并显示视频的代码。

    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    using namespace cv;
    
    #if defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
        || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
    # include <io.h>
    # include <fcntl.h>
    # define SET_BINARY_MODE(handle) setmode(handle, O_BINARY)
    #else
    # define SET_BINARY_MODE(handle) ((void)0)
    #endif
    
    #define BUFSIZE 10240
    int main ( int argc, char **argv )
    {
    
        SET_BINARY_MODE(fileno(stdin));
        std::vector<char> data;
        bool skip=true;
        bool imgready=false;
        bool ff=false;
        int readbytes=-1;
        while (1)
        {   
            char ca[BUFSIZE];
            uchar c;
            if (readbytes!=0)
            {
                readbytes=read(fileno(stdin),ca,BUFSIZE);
                for(int i=0;i<readbytes;i++)
                {
                    c=ca[i];
                    if(ff && c==(uchar)0xd8)
                    {
                        skip=false;
                        data.push_back((uchar)0xff);
                    }
                    if(ff && c==0xd9)
                    {
                        imgready=true;
                        data.push_back((uchar)0xd9);
                        skip=true;
                    }
                    ff=c==0xff;
                    if(!skip)
                    {
                        data.push_back(c);
                    }
                    if(imgready)
                    {
                        if(data.size()!=0)
                        {
                            cv::Mat data_mat(data);
                            cv::Mat frame(imdecode(data_mat,1));
    
                            imshow("frame",frame);
                            waitKey(1);
                        }else
                        {
                            printf("warning");
                        }
                        imgready=false;
                        skip=true;
                        data.clear();
                    }
                }
            }
            else
            {
                throw std::string("zero byte read");
            }
        }
    }    
    

    写输出这样的东西应该可以工作。

    void saveFramestdout(cv::Mat& frame,int compression)
    {
        SET_BINARY_MODE(fileno(stdout));
        cv::Mat towrite;
        if(frame.type()==CV_8UC1)
        {
            cvtColor(frame,towrite,CV_GRAY2BGR);
        }else if(frame.type()==CV_32FC3)
        {
            double minVal, maxVal;
            minMaxLoc(frame, &minVal, &maxVal);
            frame.convertTo(towrite, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
        }
        else{
            towrite=frame;
        }
        std::vector<uchar> buffer;
        std::vector<int> param(2);
        param[0]=CV_IMWRITE_JPEG_QUALITY;
        param[1]=compression;//default(95) 0-100
        imencode(".jpg",towrite,buffer,param);
        uchar* a = &buffer[0];
        ::write(fileno(stdout),a,buffer.size());
    }
    

    上面的问题是jpegs的多重encode/decode,可以通过与libjpeg-turbo链接部分解决。或者可以去弄清楚如何直接从 ffmpeg 和 opencv 传递原始数据。就我而言,这是完全可以接受的,因为大部分开销都在编码或视频处理中。

    【讨论】:

      【解决方案2】:

      到此为止

      我使用了 CV_FOURCC('x','2','6','4'),虽然它选择了正确的编解码器,但我的默认设置是错误的,我不确定如何正确设置它们。

      [libx264 @ 0x7f9f0b022600] broken ffmpeg default settings detected
      

      相关c++源码

      VideoCapture cap(argv[1]); // open file
      if(!cap.isOpened()) { // check if we succeeded
          cout << "failed to open file " << argv[1] << endl;
          return -1;
      }
      Mat raw;
      VideoWriter out(argv[2], CV_FOURCC('x','2','6','4'), fpsOut, size, true );
      for(;;) {
          bool bSuccess = cap.read(raw); // get a new frame from camera
          if (!bSuccess) {
              cout << "finished reading file" << endl;
              break;
          }
          out << raw;
      }
      

      【讨论】:

        【解决方案3】:

        您的设置失败,因为 ffmpeg 为您创建了 XVID 编解码器但无法操作它。因此你会得到无效的文件。

        幸运的是 XVID 编解码器已在 VfW 中注册,因此您只需删除 opencv_ffmpeg*.dll 库即可禁用它。一旦不使用ffmpeg,您的fourcc“xvid”将通过VfW机制触发正确的编解码器。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-01-28
          • 2021-05-25
          • 2013-04-09
          • 1970-01-01
          • 2023-03-16
          • 2023-03-29
          • 1970-01-01
          • 2018-11-11
          相关资源
          最近更新 更多