Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
FFmpeg-通过管道馈送原始帧-FFmpeg不检测管道闭合 我试图从Windows中的C++中学习这些例子。p>_C++_Video_Cmd_Ffmpeg_Pipe - Fatal编程技术网

FFmpeg-通过管道馈送原始帧-FFmpeg不检测管道闭合 我试图从Windows中的C++中学习这些例子。p>

FFmpeg-通过管道馈送原始帧-FFmpeg不检测管道闭合 我试图从Windows中的C++中学习这些例子。p>,c++,video,cmd,ffmpeg,pipe,C++,Video,Cmd,Ffmpeg,Pipe,我有一个应用程序,它可以生成原始帧,并用FFmpeg编码。 原始框架通过IPC管道传输至FFmpegs标准DIN。正如预期的那样,FFmpeg甚至可以显示当前可用的帧数 当我们发送完帧时,问题就出现了。当我关闭管道的写入端时,我希望FFmpeg能够检测到它,完成并输出视频。但事实并非如此。FFmpeg保持开放状态,似乎在等待更多数据 我在VisualStudio中做了一个小测试项目 #include "stdafx.h" //// stdafx.h //#include "targetver

我有一个应用程序,它可以生成原始帧,并用FFmpeg编码。 原始框架通过IPC管道传输至FFmpegs标准DIN。正如预期的那样,FFmpeg甚至可以显示当前可用的帧数

当我们发送完帧时,问题就出现了。当我关闭管道的写入端时,我希望FFmpeg能够检测到它,完成并输出视频。但事实并非如此。FFmpeg保持开放状态,似乎在等待更多数据

我在VisualStudio中做了一个小测试项目

#include "stdafx.h" 
//// stdafx.h 
//#include "targetver.h"
//#include <stdio.h>
//#include <tchar.h>
//#include <iostream>

#include "Windows.h"
#include <cstdlib>

using namespace std;

bool WritePipe(void* WritePipe, const UINT8 *const Buffer, const UINT32 Length)
{
    if (WritePipe == nullptr || Buffer == nullptr || Length == 0)
    {
        cout << __FUNCTION__ << ": Some input is useless";
        return false;
    }

    // Write to pipe
    UINT32 BytesWritten = 0;
    UINT8 newline = '\n';
    bool bIsWritten = WriteFile(WritePipe, Buffer, Length, (::DWORD*)&BytesWritten, nullptr);
    cout << __FUNCTION__ << " Bytes written to pipe " << BytesWritten << endl;
    //bIsWritten = WriteFile(WritePipe, &newline, 1, (::DWORD*)&BytesWritten, nullptr); // Do we need this? Actually this should destroy the image.

    FlushFileBuffers(WritePipe); // Do we need this?

    return bIsWritten;
}

#define PIXEL 80 // must be multiple of 8. Otherwise we get warning: Bytes are not aligned

int main()
{
    HANDLE PipeWriteEnd = nullptr;
    HANDLE PipeReadEnd = nullptr;
    {
        // create us a pipe for inter process communication
        SECURITY_ATTRIBUTES Attr = { sizeof(SECURITY_ATTRIBUTES), NULL, true };
        if (!CreatePipe(&PipeReadEnd, &PipeWriteEnd, &Attr, 0))
        {
            cout << "Could not create pipes" << ::GetLastError() << endl;
            system("Pause");
            return 0;
        }
    }

    // Setup the variables needed for CreateProcess
    // initialize process attributes
    SECURITY_ATTRIBUTES Attr;
    Attr.nLength = sizeof(SECURITY_ATTRIBUTES);
    Attr.lpSecurityDescriptor = NULL;
    Attr.bInheritHandle = true;

    // initialize process creation flags
    UINT32 CreateFlags = NORMAL_PRIORITY_CLASS;
    CreateFlags |= CREATE_NEW_CONSOLE;

    // initialize window flags
    UINT32 dwFlags = 0;
    UINT16 ShowWindowFlags = SW_HIDE;

    if (PipeWriteEnd != nullptr || PipeReadEnd != nullptr)
    {
        dwFlags |= STARTF_USESTDHANDLES;
    }

    // initialize startup info
    STARTUPINFOA StartupInfo = {
        sizeof(STARTUPINFO),
        NULL, NULL, NULL,
        (::DWORD)CW_USEDEFAULT,
        (::DWORD)CW_USEDEFAULT,
        (::DWORD)CW_USEDEFAULT,
        (::DWORD)CW_USEDEFAULT,
        (::DWORD)0, (::DWORD)0, (::DWORD)0,
        (::DWORD)dwFlags,
        ShowWindowFlags,
        0, NULL,
        HANDLE(PipeReadEnd),
        HANDLE(nullptr),
        HANDLE(nullptr)
    };

    LPSTR ffmpegURL = "\"PATHTOFFMPEGEXE\" -y -loglevel verbose -f rawvideo -vcodec rawvideo -framerate 1 -video_size 80x80 -pixel_format rgb24 -i - -vcodec mjpeg -framerate 1/4 -an \"OUTPUTDIRECTORY\"";

    // Finally create the process
    PROCESS_INFORMATION ProcInfo;
    if (!CreateProcessA(NULL, ffmpegURL, &Attr, &Attr, true, (::DWORD)CreateFlags, NULL, NULL, &StartupInfo, &ProcInfo))
    {
        cout << "CreateProcess failed " << ::GetLastError() << endl;
    }
    //CloseHandle(ProcInfo.hThread);


        // Create images and write to pipe
#define MYARRAYSIZE (PIXEL*PIXEL*3) // each pixel has 3 bytes

    UINT8* Bitmap = new UINT8[MYARRAYSIZE];

    for (INT32 outerLoopIndex = 9; outerLoopIndex >= 0; --outerLoopIndex)   // frame loop
    {
        for (INT32 innerLoopIndex = MYARRAYSIZE - 1; innerLoopIndex >= 0; --innerLoopIndex) // create the pixels for each frame
        {
            Bitmap[innerLoopIndex] = (UINT8)(outerLoopIndex * 20); // some gray color
        }
        system("pause");
        if (!WritePipe(PipeWriteEnd, Bitmap, MYARRAYSIZE))
        {
            cout << "Failed writing to pipe" << endl;
        }
    }

    // Done sending images. Tell the other process. IS THIS NEEDED? HOW TO TELL FFmpeg WE ARE DONE?
    //UINT8 endOfFile = 0xFF; // EOF = -1 == 1111 1111 for uint8
    //if (!WritePipe(PipeWriteEnd, &endOfFile, 1))
    //{
    //  cout << "Failed writing to pipe" << endl;
    //}
    //FlushFileBuffers(PipeReadEnd); // Do we need this?

    delete Bitmap;


    system("pause");

    // clean stuff up
    FlushFileBuffers(PipeWriteEnd); // Do we need this?

    if (PipeWriteEnd != NULL && PipeWriteEnd != INVALID_HANDLE_VALUE)
    {
        CloseHandle(PipeWriteEnd);
    }

    // We do not want to destroy the read end of the pipe? Should not as that belongs to FFmpeg
    //if (PipeReadEnd != NULL && PipeReadEnd != INVALID_HANDLE_VALUE)
    //{
    //  ::CloseHandle(PipeReadEnd);
    //}


    return 0;

}
正如您在te FFmpeg输出的最后一行中所看到的那样,这些图像是经过处理的。10帧可用。但是关闭管道后,FFmpeg不会关闭,仍然需要输入

正如链接的示例所示,这应该是一种有效的方法


现在试了一个星期

我终于放弃了。不知道是什么问题。改为使用套接字和TCP。在TCP连接的命令行下方

LPSTR ffmpegURL = "\"PATHTOFFMPEGEXE\" -y -loglevel verbose -f rawvideo -vcodec rawvideo -framerate 1 -video_size 80x80 -pixel_format rgb24 -i tcp://127.0.0.1:PORT_NR -vcodec mjpeg -framerate 1/4 -an \"OUTPUTDIRECTORY\"";

在这种情况下,FFmpeg是客户端并尝试连接到服务器。

创建管道时,它会生成两个句柄,一个用于读取,一个用于写入:

HANDLE readPipe;
HANDLE writePipe;

SECURITY_ATTRIBUTES sa  = { sizeof(sa) };
sa.lpSecurityDescriptor = nullptr;
sa.bInheritHandle       = TRUE;

CreatePipe(&readPipe, &writePipe, &sa, sizeof(buffer));
请注意
bInheritHandle=TRUE
标志。这允许生成的子进程(ffmpeg)继承句柄,这是它访问管道所必需的(下面的
CreateProcess
函数签名中有一个类似的参数,
bInheritHandles
,也设置为
TRUE

然后启动子进程并将其
stdin
映射到
readPipe

STARTUPINFOA si = { sizeof(si) };
si.dwFlags      = STARTF_USESTDHANDLES;
si.hStdInput    = readPipe;
si.hStdOutput   = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError    = GetStdHandle(STD_ERROR_HANDLE);

PROCESS_INFORMATION pi = {};

CreateProcessA(nullptr, cmd, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi);
此时,您可以开始通过
WriteFile
将帧压缩到ffmpeg,该文件(如果被告知使用
stdin
的输入)将进行编码。问题是当你做完了,打电话给

CloseHandle(readPipe);
CloseHandle(writePipe);
..ffmpeg一直在等待更多数据,而不是关闭文件

原因如下:

创建子进程时,它将继承所有句柄,而不仅仅是在
STARTUPINFO
结构(即
readPipe
)中指定的句柄。因此,它还将继承
writepe
句柄。对于从
readPipe
读取以生成EOF、发送ffmpeg完成并关闭的信号,必须首先关闭
writepe
。但是,由于子进程具有两个句柄的副本,因此即使在您端关闭这对句柄,也不会触发客户端上的EOF,因为
writepe
仍然保持活动状态

修复方法是防止继承
writepe
。与此类似(在生成子进程之前):

下面是一个完整的工作源代码示例(输出一个简短的测试电影):

#包括
char*cmd=“ffmpeg-f rawvideo-pix\u fmt rgba-video\u大小1920x1080-r30-i管道:0-预设快速-y-pix\u fmt yuv420p输出.mp4”;
整数缓冲区[1920*1080];
void main()
{
处理读取管;
手柄;
安全属性sa={sizeof(sa)};
sa.lpSecurityDescriptor=nullptr;
sa.bInheritHandle=TRUE;
CreatePipe(&readPipe,&writePipe,&sa,sizeof(buffer));
SetHandleInformation(WritePie,HANDLE\u FLAG\u INHERIT,0);
StartupInfoaSi={sizeof(si)};
si.dwFlags=STARTF_USESTDHANDLES;
si.hStdInput=读取管;
si.hStdOutput=GetStdHandle(标准输出句柄);
si.hStdError=GetStdHandle(标准错误句柄);
进程信息pi={};
CreateProcessA(nullptr、cmd、nullptr、nullptr、TRUE、0、nullptr、nullptr、si和pi);
用于(int帧=0;帧<1080;帧++)
{
对于(int x=0;x<1920;x++)
{
缓冲区[帧*1920+x]=0xffffff00 |(帧*255/1079);
}
德沃德·拜特斯莱特;
WriteFile(writepe、buffer、sizeof(buffer)和byteswrite、nullptr);
}
关闭手柄(读取管);
关闭手柄(WritePie);
}

@rustyx这有什么帮助?我在frustation外试过,但没有成功。我看没问题——你试过发送几百帧吗?@MarkSetchell yah试过。没有变化。您也可以直接在命令窗口中尝试该命令行(使用合适的路径),将-video_size设置为较小的值,这样您就不必输入太多,只需将一些垃圾数据写入STDIN即可。按ENTER键后,FFmpeg仍侦听输入。好吧,这就是我希望它能做的,因为在这种情况下,标准管道没有关闭。但你会怎么告诉FFmpeg我们在这件事上完蛋了?多次点击STRG+Z使FFmpeg退出并输出视频。STRG+Z是EOF。我尝试通过程序发送EOF,但没有结果。谢谢。没有尝试你的代码,但我仍然接受它作为答案。
CloseHandle(readPipe);
CloseHandle(writePipe);
SetHandleInformation(writePipe, HANDLE_FLAG_INHERIT, 0);
#include <windows.h>

char* cmd = "ffmpeg -f rawvideo -pix_fmt rgba -video_size 1920x1080 -r 30 -i pipe:0 -preset fast -y -pix_fmt yuv420p output.mp4";
int   buffer[1920 * 1080];

void main()
{
    HANDLE readPipe;
    HANDLE writePipe;

    SECURITY_ATTRIBUTES sa  = { sizeof(sa) };
    sa.lpSecurityDescriptor = nullptr;
    sa.bInheritHandle       = TRUE;

    CreatePipe(&readPipe, &writePipe, &sa, sizeof(buffer));

    SetHandleInformation(writePipe, HANDLE_FLAG_INHERIT, 0);

    STARTUPINFOA si = { sizeof(si) };
    si.dwFlags      = STARTF_USESTDHANDLES;
    si.hStdInput    = readPipe;
    si.hStdOutput   = GetStdHandle(STD_OUTPUT_HANDLE);
    si.hStdError    = GetStdHandle(STD_ERROR_HANDLE);

    PROCESS_INFORMATION pi = {};

    CreateProcessA(nullptr, cmd, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi);

    for (int frame = 0; frame < 1080; frame++)
    {
        for (int x = 0; x < 1920; x++)
        {
            buffer[frame * 1920 + x] = 0xffffff00 | (frame * 255 / 1079);
        }

        DWORD bytesWritten;
        WriteFile(writePipe, buffer, sizeof(buffer), &bytesWritten, nullptr);
    }

    CloseHandle(readPipe);
    CloseHandle(writePipe);
}