用管道从CMD窗口读FFMPEG执行信息的方法
From Ffmpeg工程组
用管道从CMD窗口读FFMPEG执行信息的方法
从www.vckbase.com上面看到管理与CMD通信的方法,正好适用于FFMPEG,当然也适用于MENCODER等所有CMD窗口程序。
void function()
{
HANDLE hSTDINWrite, hSTDINRead; // 用于重定向子进程输入的句柄
HANDLE hSTDOUTWrite, hSTDOUTRead; // 用于重定向子进程输出的句柄
HANDLE hSTDERRWrite, hSTDERRRead; // 用于重定向子进程输出的句柄
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(sa);
// 创建子进程输出匿名管道
if( !CreatePipe(&hSTDOUTRead, &hSTDOUTWrite, &sa, 0) )
{
//AfxMessageBox("Create STDOUT pipe failed");
return;
}
// 创建子进程输入匿名管道
if( !CreatePipe(&hSTDINRead, &hSTDINWrite, &sa, 0) )
{
// AfxMessageBox("Create STDIN pipe failed");
return;
}
// 创建子进程出错匿名管道
/* if( !CreatePipe(&hSTDERRRead, &hSTDERRWrite, &sa, 0) )
{
AfxMessageBox("Create STDIN pipe failed");
return;
} */
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
STARTUPINFO si;
GetStartupInfo(&si);
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;//SW_HIDE;
si.hStdInput = hSTDINRead; //重定向子进程输入
si.hStdOutput = hSTDOUTWrite; //重定向子进程输入
si.hStdError = hSTDOUTWrite; //GetStdHandle( STD_ERROR_HANDLE );
char cmd[256] = {0, };
//::strcpy(cmd, "d:\\mencoder -oac copy -ovc copy -o d:\\aaa3.mpg d:\\2.mpg");
::strcpy(cmd, "e:\\ffmpeg\\trunk\\ffmpeg -i d:\\1.mpg d:\\1.avi");
//if( !::CreateProcess(NULL, cmd, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW, NULL, NULL, &si, &pi) )
if( !::CreateProcess(NULL, cmd, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) )
{
int errorNo = ::GetLastError();
// AfxMessageBox("create process failed");
return;
}
::CloseHandle(hSTDOUTWrite);
::CloseHandle(hSTDINRead);
char strData[1024] = {0, };
DWORD dwBytes;
while(::ReadFile(hSTDOUTRead, strData, sizeof(strData), &dwBytes, NULL))
{
ProcessMessage();
strData[dwBytes] = '\0';
//SetMessage(strData);
if(m_bCancel)
{
//::WriteFile(hSTDOUTWrite, "q", 2, &dwBytes, NULL);
break;
}
}
DWORD uExitCode ;
::TerminateProcess(pi.hProcess,uExitCode);
//raise(3);
//abort();
// ::WaitForSingleObject(pi.hProcess, INFINITE);
::CloseHandle(hSTDOUTRead);
::CloseHandle(hSTDINWrite);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
}
但是用了上面的方法可是在转换文件过程中(转化结束之前),会造成程序假死现象
假死是因为主线程(进程所在线程)被阻塞,不能响应消息循环,所以就假死了,解决办法是创建一个独立的工作线程来完成ffmpeg的转换任务,与主线程通过线 程消息、信号量等进行通信或同步。
所以如果某些片子会假死, ffmpeg进程明明完成转换,可 WaitForSingleObject(ProcessInfor.hProcess, 10) == WAIT_OBJECT_0, 总是等不到进程结 束信号,ffmpeg.exe进程始终存在,不用管道或者换某些其它片子就ok了,看来像微软的bug.
可采用以下方式解决:
source code:
HANDLE hReadPipe;
HANDLE hWritePipe;
SECURITY_ATTRIBUTES Security_Attributes;
Security_Attributes.nLength=sizeof(SECURITY_ATTRIBUTES);
Security_Attributes.lpSecurityDescriptor=NULL;
Security_Attributes.bInheritHandle=TRUE;
CreatePipe(&hReadPipe, &hWritePipe, &Security_Attributes, 0);
STARTUPINFO StartupInfo;
GetStartupInfo(&StartupInfo);
// StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; //removed for strange bug
StartupInfo.wShowWindow = SW_HIDE;
StartupInfo.hStdOutput = hWritePipe;
StartupInfo.hStdError = hWritePipe;
StartupInfo.hStdInput = hReadPipe;
PROCESS_INFORMATION ProcessInfor = {0};
if(!CreateProcess(NULL, strCmdLine.GetBuffer(0),
NULL,NULL,TRUE,
CREATE_SUSPENDED,NULL,NULL,
&StartupInfo,&ProcessInfor))
{
MessageBox(_T("Can't run ffmpeg.exe, Check if missing."), _T("Error"));
return FALSE;
}
DWORD ProcessTime = GetTickCount();
ResumeThread(ProcessInfor.hThread);
m_isExitReady = FALSE;
DWORD SizeRead;
TCHAR OutputData[4096];
DWORD TotalBytesAvail;
DWORD BytesLeftThisMessage;
MSG msg;
m_cProgress.SetRange(0, 1000);
m_cProgress.SetStep(1);
#ifdef ENABLE_LOG
CFile LogFile;//create log file if needed
LogFile.Open("converter.log", CFile::modeCreate|CFile::modeWrite);
#endif //ENABLE_LOG
do
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(PeekNamedPipe(hReadPipe, OutputData, 4096, &SizeRead,
&TotalBytesAvail, &BytesLeftThisMessage))
{
//get wanted data here.
#ifdef ENABLE_LOG
LogFile.Write(OutputData, SizeRead);
#endif //ENABLE_LOG
}
m_cProgress.StepIt();
}
while(WaitForSingleObject(ProcessInfor.hProcess, 10) != WAIT_OBJECT_0);
#ifdef ENABLE_LOG
LogFile.Close();
#endif //ENABLE_LOG
m_cProgress.SetPos(1000);
CloseHandle(ProcessInfor.hProcess);
CloseHandle(ProcessInfor.hThread);
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
m_isExitReady = TRUE;
ProcessTime = GetTickCount() - ProcessTime;
m_ProcessTime.Format("Cost %d ms", ProcessTime);
UpdateData(FALSE);
return CheckOutput();
有关该问题的讨论帖可参考ffmpeg工程组论坛中的相关讨论:
有关用管道从CMD窗口读FFMPEG执行信息的方法的讨论
