簡體   English   中英

如何將幀從 OpenCV C++ 代碼流式傳輸到 Video4Linux 或 ffmpeg?

[英]How to stream frames from OpenCV C++ code to Video4Linux or ffmpeg?

我正在嘗試使用 OpenCV 來處理來自視頻流的幀。 目標是從流中獲取幀,對其進行處理,然后將處理后的幀放入新的/新鮮的流中。

我已經能夠使用 OpenCV 視頻捕獲功能成功讀取流。 但不知道如何使用已處理的幀創建輸出流。

為了進行一些基本測試,我使用 ffmpeg 從本地視頻文件創建了一個流,如下所示:

ffmpeg -i sample.mp4 -v 0 -vcodec mpeg4 -f mpegts \
        "udp://@127.0.0.1:23000?overrun_nonfatal=1&fifo_size=50000000"

在我使用 OpenCV 庫的 VideoCapture 功能的 C++ 代碼中,我能夠捕獲上面創建的流。 下面附上了我要實現的基本布局:

cv::VideoCapture capture("udp://@127.0.0.1:23000?overrun_nonfatal=1&fifo_size=50000000", cv::CAP_FFMPEG);

cv::Mat frame;

while (true) 
{
    // use the above stream to capture a frame
    capture >> frame;
    
    // process the frame (not relevant here)
    ...

    // finally, after all the processing is done I 
    // want to put this frame on a new stream say at
    // udp://@127.0.0.1:25000, I don't know how to do
    // this, ideally would like to use Video4Linux but
    // solutions with ffmpeg are appreciated as well
}

正如您從上面代碼中的評論中看到的那樣,我什至不知道應該如何開始處理這個問題,我嘗試搜索類似的問題,但我能找到的只是如何使用流進行 VideoCapture,與輸出到溪流。

我對此比較陌生,對你們中的許多人來說,這似乎是一個非常基本的問題,請原諒。

我們可以使用與下面的 Python 代碼示例相同的技術。

  • 將FFmpeg作為子進程執行,打開stdin管道進行寫入

     FILE *pipeout = popen(ffmpeg_cmd.c_str(), "w")
  • frame.data寫入 FFmpeg 子進程的 stdin 管道(循環中)

     fwrite(frame.data, 1, width*height*3, pipeout);
  • 最后關閉管道(它將關閉子進程)

     pclose(pipeout);

以下示例是一個通用示例 - 構建編號幀,並將編碼視頻寫入 MKV 輸出文件。

該示例使用以下等效命令行:
ffmpeg -y -f rawvideo -r 10 -video_size 320x240 -pixel_format bgr24 -i pipe: -vcodec libx264 -crf 24 -pix_fmt yuv420p output.mkv

您可以根據您的特定要求調整參數(將output.mkv替換為udp://@127.0.0.1:25000 )。

capture >> frame替換編號的幀,並調整大小和幀率。


代碼示例:

#include <stdio.h>
#include <chrono>
#include <thread>
#include "opencv2/opencv.hpp"

int main()
{
    int width = 320;
    int height = 240;
    int n_frames = 100;
    int fps = 10;

    //Use a "generic" example (write the output video in output.mkv video file).
    //ffmpeg -y -f rawvideo -r 10 -video_size 320x240 -pixel_format bgr24 -i pipe: -vcodec libx264 -crf 24 -pix_fmt yuv420p output.mkv
    std::string ffmpeg_cmd = std::string("ffmpeg -y -f rawvideo -r ") + std::to_string(fps) +
                             " -video_size " + std::to_string(width) + "x" + std::to_string(height) +
                             " -pixel_format bgr24 -i pipe: -vcodec libx264 -crf 24 -pix_fmt yuv420p output.mkv";

    //Execute FFmpeg as sub-process, open stdin pipe (of FFmpeg sub-process) for writing.
    //In Windows we need to use _popen and in Linux popen
#ifdef _MSC_VER
    FILE *pipeout = _popen(ffmpeg_cmd.c_str(), "wb");   //Windows (ffmpeg.exe must be in the execution path)
#else
    //https://batchloaf.wordpress.com/2017/02/12/a-simple-way-to-read-and-write-audio-and-video-files-in-c-using-ffmpeg-part-2-video/
    FILE *pipeout = popen(ffmpeg_cmd.c_str(), "w");     //Linux (assume ffmpeg exist in /usr/bin/ffmpeg (and in path).
#endif

    for (int i = 0; i < n_frames; i++)
    {
        cv::Mat frame = cv::Mat(height, width, CV_8UC3);
        frame = cv::Scalar(60, 60, 60); //Fill background with dark gray
        cv::putText(frame, std::to_string(i+1), cv::Point(width/2-50*(int)(std::to_string(i+1).length()), height/2+50), cv::FONT_HERSHEY_DUPLEX, 5, cv::Scalar(30, 255, 30), 10);  // Draw a green number

        cv::imshow("frame", frame);cv::waitKey(1); //Show the frame for testing

        //Write width*height*3 bytes to stdin pipe of FFmpeg sub-process (assume frame data is continuous in the RAM).
        fwrite(frame.data, 1, width*height*3, pipeout);
    }

    // Flush and close input and output pipes
    fflush(pipeout);

#ifdef _MSC_VER
    _pclose(pipeout);   //Windows
#else
    pclose(pipeout);    //Linux
#endif

    //It looks like we need to wait one more second at the end. //https://stackoverflow.com/a/62804585/4926757
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // sleep for 1 second

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM