使用 ffmpeg 从 C++ 内存中的多个图像流式传输 RTSP

Posted

技术标签:

【中文标题】使用 ffmpeg 从 C++ 内存中的多个图像流式传输 RTSP【英文标题】:Streaming RTSP from multiple images from memory in C++ using ffmpeg 【发布时间】:2021-12-26 05:00:02 【问题描述】:

我正在编写一个在屏幕上显示文本的应用程序。我希望能够通过 RTSP 流式传输屏幕(带有文本),以便像 OBS/vMix 或 VLC 这样的程序可以捕获它。我要做的是每 N 毫秒(比如每 100 毫秒)截取一次屏幕截图,将其保存到内存(而不是磁盘)中,然后将其写入到 ffmpeg 的管道中。然后ffmpeg 获取这些图像并从中生成 RTSP 流。我已经实现了每 N 毫秒截取一次屏幕截图的部分,并将其写入管道(类似于this 问题,不同之处在于我将图像保存在内存中,而不是磁盘上)。现在的问题是我对ffmpeg 不太了解(我已将参数设置为this 问题的答案,但是当我尝试通过VLC 或其他程序播放流时,屏幕上什么也看不到)。我可以在日志中看到很多Successfully sent a screenshot,但在实际流中什么也没有。你能告诉我我做错了什么吗?

你可以找到整个项目here

这是我的(部分)代码:

bool RTSP_SERVER_SHOULD_START = false;
tthread::mutex rtsp_server_mutex;
tthread::mutex screenshot_mutex;
tthread::thread* rtsp;
FILE *pPipe;
bool send_screenshot = false;

int main(int argc, char *argv[]) 
// OpenGL stuff...
        rtsp_server_mutex.lock();
        if (RTSP_SERVER_SHOULD_START) 
            RTSP_SERVER_SHOULD_START = false;
            rtsp = new tthread::thread(rtspScreenShotToPipe, 0);
            if( (pPipe = _popen( "ffmpeg -re -f image2pipe -vcodec mjpeg -i - -vcodec h264 -r 10 -f mpegts udp://127.0.0.1:1234", "wb")) == NULL ) 
                cerr << "Error: Could not open ffmpeg" << endl;
                _pclose(pPipe);
                exit(1);
            
        
        rtsp_server_mutex.unlock();

    while (!glfwWindowShouldClose(WINDOW)) 
        // More OpenGL stuff
        screenshot_mutex.lock();
        if(send_screenshot) 
            send_screenshot = false;
            // Make the BYTE array, factor of 3 because it's RBG.
            BYTE* pixels = new BYTE[3 * DEFAULT_MONITOR.maxResolution.width * DEFAULT_MONITOR.maxResolution.height];

            glReadPixels(0, 0, DEFAULT_MONITOR.maxResolution.width, DEFAULT_MONITOR.maxResolution.height, GL_RGB, GL_UNSIGNED_BYTE, pixels);

            // Convert to FreeImage format & save to file
            FIBITMAP* image = FreeImage_ConvertFromRawBits(pixels, DEFAULT_MONITOR.maxResolution.width, DEFAULT_MONITOR.maxResolution.height, 3 * DEFAULT_MONITOR.maxResolution.width, 24, 0x0000FF, 0xFF0000, 0x00FF00, false);

            FreeImageIO io;
            SetDefaultIO(&io);

            bool success = FreeImage_SaveToHandle(FIF_JPEG, image, &io, (fi_handle)pPipe);
            fflush(pPipe);
            if(success) 
                cout << "Successfully sent a screenshot" << endl;
            

            // Free resources
            FreeImage_Unload(image);
            delete [] pixels;
        
        screenshot_mutex.unlock();
    
    printf("%d", glGetError());

    glfwTerminate();
    return 0;


void rtspScreenShotToPipe(void * aArg) 
    while(true) 
        rtsp_server_mutex.try_lock();
        if(!RTSP_SERVER_STARTED) 
            break;
        
        rtsp_server_mutex.unlock();
        tthread::this_thread::sleep_for(tthread::chrono::milliseconds(100));
        screenshot_mutex.try_lock();
        send_screenshot = true;
        screenshot_mutex.unlock();
    


// I've copied those function from FreeImage in my code

void SetDefaultIO(FreeImageIO *io) 
    io->read_proc  = _ReadProc;
    io->seek_proc  = _SeekProc;
    io->tell_proc  = _TellProc;
    io->write_proc = _WriteProc;


unsigned DLL_CALLCONV _ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) 
    return (unsigned)fread(buffer, size, count, (FILE *)handle);


unsigned DLL_CALLCONV _WriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) 
    return (unsigned)fwrite(buffer, size, count, (FILE *)handle);


int DLL_CALLCONV _SeekProc(fi_handle handle, long offset, int origin) 
    return fseek((FILE *)handle, offset, origin);


long DLL_CALLCONV _TellProc(fi_handle handle) 
    return ftell((FILE *)handle);


【问题讨论】:

【参考方案1】:

您没有使用 RTSP,而是通过 UDP 发送 MPEG-TS。

在 VLC 中确保您侦听正确的网络流:

udp://@127.0.0.1:1234

在 FFmpeg 方面,您需要指定有效负载大小:

-f mpegts udp://127.0.0.1:1234?pkt_size=1316

FFmpeg 有一个可以直接从 C++ 使用的 API,因此您不必通过管道传输到进程。

【讨论】:

那个 API 是什么?有链接吗? @bennyb 文档:ffmpeg.org/doxygen/trunk/index.html,示例:ffmpeg.org/doxygen/trunk/examples.html 非常感谢!知道我是否可以流式传输到 RTSP 而不是 UDP?如果是,怎么做? @bennyb 你需要一个 RTSP 服务器。请参阅ffmpeg.org/ffmpeg-protocols.html#rtsp 了解更多信息。 非常感谢!

以上是关于使用 ffmpeg 从 C++ 内存中的多个图像流式传输 RTSP的主要内容,如果未能解决你的问题,请参考以下文章

ffmpeg 从具有多个通道的输入中输出单独的通道

ffmpeg 在尝试用图像制作幻灯片时抛出“输出文件 #0 不包含任何流”

ffmpeg 捕获当前帧并覆盖图像输出文件

关于ffmpeg推流,如何推流一个文件夹里的所有视频或者多个视频?

从 OpenCV(C++)中的目录读取多个图像

Android使用FFMpeg实现推送视频直播流到服务器