弄了三天都没有弄成功过。。就是用线程启动一个外部程序FFMPEG.EXE,传入命令让它关闭,这个外部程序只要按一个q键和CTRL+C就会自动关闭.如果没有执行完直接关进程的话转一半的来的视频就不能播放。
代码如下:
procedure TForm2.Button1Click(Sender: TObject);\\启动程序是在后台运行的
begin
ssdcwt.Create(false);\\调用下面的线程end;procedure TForm2.Button1Click(Sender: TObject);\\发送命令给正在转换的外部程序
begin
//sendmessage()用这个不行;
//postmessage()也不行;
不知道那个位高手能帮我解决一下,困扰了我三天了,只要按一下按钮就是正常停止而不是强行关闭。。
end;
这是一个线程
unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;type
ssdcwt = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;implementationuses Unit2, TlHelp32, pThread;{ Important: Methods and properties of objects in visual components can only be
used in a method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, procedure ssdcwt.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }{ ssdcwt }
procedure MyRun_pipe(cmd: String; var m: TMemo);
varhReadPipe,hWritePipe:THandle;
si:STARTUPINFO;
lsa:SECURITY_ATTRIBUTES;
pi:PROCESS_INFORMATION;
cchReadBuffer:DWORD;
ph:PChar;
fname:PChar;
begintry
Screen.Cursor :=crDefault;
fname:=allocmem(255);
ph:=AllocMem(5000);
lsa.nLength :=sizeof(SECURITY_ATTRIBUTES);
lsa.lpSecurityDescriptor :=nil;
lsa.bInheritHandle :=True;if CreatePipe(hReadPipe,hWritePipe,@lsa,0)=false then
begin
ShowMessage('Can not create pipe!');
exit;
end;fillchar(si,sizeof(STARTUPINFO),0);
si.cb :=sizeof(STARTUPINFO);
si.dwFlags :=(STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW);
si.wShowWindow :=SW_HIDE; //SW_show;
si.hStdOutput :=hWritePipe;
si.hStdInput :=hWritePipe;
si.hStdError :=hWritePipe;
StrPCopy(fname,cmd);
if CreateProcess( nil, fname, nil, nil, true, 0, nil, nil, si, pi) = False then
begin
ShowMessage('can not create process');
FreeMem(ph);
FreeMem(fname);
Exit;
end;
while(true) do
begin
if not PeekNamedPipe(hReadPipe,ph,1,@cchReadBuffer,nil,nil) then break;
if cchReadBuffer<>0 then
begin
if ReadFile(hReadPipe,ph^,4096,cchReadBuffer,nil)=false then break;
ph[cchReadbuffer]:=chr(0);
m.Lines.Add(ph); //关键就是在新进程结束前输出
end
else if(WaitForSingleObject(pi.hProcess ,0)=WAIT_OBJECT_0)//等待cmd结束
then break;
end;
ph[cchReadBuffer]:=chr(0);
m.Lines.Add(ph);
CloseHandle(hReadPipe);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hWritePipe);
FreeMem(ph);
FreeMem(fname);
finally
Screen.Cursor := crDefault;
end;
end;
procedure ssdcwt.Execute;var
ssdcwt1:string;
begin
ssdcwt1:='ffmpeg.exe -y -i D:\1.avi -bitexact -vcodec mpeg4 -fixaspect -s 320x240 -r 14.985 -b 190 -acodec aac -ac 1 -ar 16000 -ab 48 -f 3gp -muxvb 64 -muxab 32 ssdcwt.3gp';
myrun_pipe(ssdcwt1,form2.Memo1);
end;
end.
代码如下:
procedure TForm2.Button1Click(Sender: TObject);\\启动程序是在后台运行的
begin
ssdcwt.Create(false);\\调用下面的线程end;procedure TForm2.Button1Click(Sender: TObject);\\发送命令给正在转换的外部程序
begin
//sendmessage()用这个不行;
//postmessage()也不行;
不知道那个位高手能帮我解决一下,困扰了我三天了,只要按一下按钮就是正常停止而不是强行关闭。。
end;
这是一个线程
unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;type
ssdcwt = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;implementationuses Unit2, TlHelp32, pThread;{ Important: Methods and properties of objects in visual components can only be
used in a method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, procedure ssdcwt.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }{ ssdcwt }
procedure MyRun_pipe(cmd: String; var m: TMemo);
varhReadPipe,hWritePipe:THandle;
si:STARTUPINFO;
lsa:SECURITY_ATTRIBUTES;
pi:PROCESS_INFORMATION;
cchReadBuffer:DWORD;
ph:PChar;
fname:PChar;
begintry
Screen.Cursor :=crDefault;
fname:=allocmem(255);
ph:=AllocMem(5000);
lsa.nLength :=sizeof(SECURITY_ATTRIBUTES);
lsa.lpSecurityDescriptor :=nil;
lsa.bInheritHandle :=True;if CreatePipe(hReadPipe,hWritePipe,@lsa,0)=false then
begin
ShowMessage('Can not create pipe!');
exit;
end;fillchar(si,sizeof(STARTUPINFO),0);
si.cb :=sizeof(STARTUPINFO);
si.dwFlags :=(STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW);
si.wShowWindow :=SW_HIDE; //SW_show;
si.hStdOutput :=hWritePipe;
si.hStdInput :=hWritePipe;
si.hStdError :=hWritePipe;
StrPCopy(fname,cmd);
if CreateProcess( nil, fname, nil, nil, true, 0, nil, nil, si, pi) = False then
begin
ShowMessage('can not create process');
FreeMem(ph);
FreeMem(fname);
Exit;
end;
while(true) do
begin
if not PeekNamedPipe(hReadPipe,ph,1,@cchReadBuffer,nil,nil) then break;
if cchReadBuffer<>0 then
begin
if ReadFile(hReadPipe,ph^,4096,cchReadBuffer,nil)=false then break;
ph[cchReadbuffer]:=chr(0);
m.Lines.Add(ph); //关键就是在新进程结束前输出
end
else if(WaitForSingleObject(pi.hProcess ,0)=WAIT_OBJECT_0)//等待cmd结束
then break;
end;
ph[cchReadBuffer]:=chr(0);
m.Lines.Add(ph);
CloseHandle(hReadPipe);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hWritePipe);
FreeMem(ph);
FreeMem(fname);
finally
Screen.Cursor := crDefault;
end;
end;
procedure ssdcwt.Execute;var
ssdcwt1:string;
begin
ssdcwt1:='ffmpeg.exe -y -i D:\1.avi -bitexact -vcodec mpeg4 -fixaspect -s 320x240 -r 14.985 -b 190 -acodec aac -ac 1 -ar 16000 -ab 48 -f 3gp -muxvb 64 -muxab 32 ssdcwt.3gp';
myrun_pipe(ssdcwt1,form2.Memo1);
end;
end.
解决方案 »
- 有操作FLV的控件吗
- 有没有好心人帮我改改这个delphi源码啊?我是毕业设计急用.实在是不懂delphi.拜托拉
- 那里有delphi6简体中文版下载阿?
- 关于StoredProc的初学者问题
- Anycell Report(中国式报表)的最新版本V1.7出来啦,支持交叉表和分组报表,欢迎下载!
- 如何控制按扭的覆盖问题?
- DbExpress控件连接oracle数据库时,不用输入用户名和密码(或者输入不正确)都提示数据库连接成功?我像加入一段代码控制输入正确用户名和密码才能登陆系统
- 怎么样把另外一个窗口显示在主窗口的panel里面!
- 利用ImageEN控件扫描多页的问题
- 小数点保留问题
- Delphi中如何进行protel的二次开发(急)!
- 在TWebBrowser中屏蔽Scripts所弹出的对对话框
改成si.dwFlags :=STARTF_USESHOWWINDOW;下面发送一个q就可以关闭程序但是这个程序的输出信息就不能在Memo1里面显示而是在自己的窗口显示
procedure TForm2.Button1Click(Sender: TObject);\\发送命令给正在转换的外部程序
begin
//sendmessage(H,WM_CHAR,113,0);这样就能正常关闭。
END;
有什么办法可以解决这个问题。。
可以用PostThreadMessage.
.......
const Msg_Terminate =100;
type
ssdcwt = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end; implementation uses Unit1, TlHelp32, pThread;
......
procedure ssdcwt.Execute;
var
Msg :TMsg;
bEnd :boolean;
begin
bEnd:=false;
while true do
begin
if (PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) then
begin
case msg.message of
Msg_Terminate:
begin
bEnd:=true;
end;
end;
end; if(bEnd) then exit; sleep(100);
end;
end;
==================================================================
//----------------------------------------------------------
procedure TForm2.Button2Click(Sender: TObject);
begin
//sendmessage()用这个不行;
//postmessage()也不行;
//不知道那个位高手能帮我解决一下,困扰了我三天了,只要按一下按钮就是正常停止而不是强行关闭。。
PostThreadMessage( pssdcwt.ThreadID,Msg_Terminate,0,0);
end;========================================================================
..........
var
Form2: TForm2;
pssdcwt:ssdcwt; //线程指针 implementation {$R *.dfm} procedure TForm2.Button1Click(Sender: TObject);
begin
pssdcwt:= ssdcwt.Create(false);//调用下面的线程
end;
.................
=========================================================老大你的方法不行呀这个全局变量 pssdcwt:ssdcwt;//线程指针 放上去设置有误,赋值线程指针语法有误不知道谁知道怎么写。。帮帮我
还有这句放if(PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) then
返回的都是FALSE。不知道那里有问题这个方法好像行不通你能不能帮我试一下。我是用线程启动外部程序的FFMPEG.EXE就算把线程关了外部程序能正常的结束吗?
有谁能告诉我帮我想个办法。。
如果直接把进程关了。那么转换出来的视频就不能播放我是用线程调用FFMPEG.EXE把外部程序正在转换的输出信息(也就是这个程序运行壮态)读到memo1里面。。而如果我把下面的句话改一下就可以发送Q让程序关闭。但是这样做我的外部程序输出信息就读不到了。。
如果把si.dwFlags :=(STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW);
改成si.dwFlags :=STARTF_USESHOWWINDOW; 下面发送一个q就可以关闭程序但是这个程序的输出信息就不能在Memo1里面显示而是在自己的窗口显示
procedure TForm2.Button1Click(Sender: TObject);\\发送命令给正在转换的外部程序
begin
sendmessage(H,WM_CHAR,113,0);这样就能正常关闭。 转换出来的视频就可以放
END;
有什么办法可以解决这个问题。。
还有一个问题就是如果我没有把外部程序隐藏,,那个FFMPEG这个程序的窗口没有任务东西显示。因为我把信息都读到MEMO1里面去了。。我选中外部程序的窗口,Q键没有反应。。但是我按一下ctrl+c他能停止谁能帮我看看。。已经一个多星期过去了。问题还是没有解决。。高手。。帮帮我吧。。
现在的问题是我把这个外部程序的输入信息读到Memo1里面去了。这个外部程序就是一个窗口里面什么东西都没有都读到MEMO1里面来了这个如果我给这个外部程序发消息Q没有反应。。如果我没有把外部程序的信息读到MEMO1里面去。直接在窗口上显示这样我发送消息Q他就能自动关闭。而且转换到一半就停止的视频可以播器。 如果把外部信息都读到MEMO1里面去了。发消息给外部程序没有一点反应所以问题就在这里。我把外部程序返回的信息读到MOME1里面去有什么办法可以让他在转换当中正常停。也就是发送消息。能让他停止也不是直接关进程。。
我说的够清楚了吧。你可以试一下我的代码
现在的问题是我把这个外部程序的输入信息读到Memo1里面去了。这个外部程序就是一个窗口里面什么东西都没有都读到MEMO1里面来了这个如果我给这个外部程序发消息Q没有反应。。如果我没有把外部程序的信息读到MEMO1里面去。直接在窗口上显示这样我发送消息Q他就能自动关闭。而且转换到一半就停止的视频可以播器。 如果把外部信息都读到MEMO1里面去了。发消息给外部程序没有一点反应所以问题就在这里。我把外部程序返回的信息读到MOME1里面去有什么办法可以让他在转换当中正常停。也就是发送消息。能让他停止也不是直接关进程。。
我说的够清楚了吧。你可以试一下我的代码
2 重新设计 decode_interrupt_cb(),通过判断一个全局变量 ffmpeg_ctrl,实现中止
3 设计一个函数 ffmpeg_quit(),设置全局变量 ffmpeg_ctrl
4 编译成 DLL,导出 ffmpeg_main 和 ffmpeg_quit
5 通过调用这两个函数实现启动和停止(要在两个不同的线程中调用)也可以不用全局变量,用全局EVENT等实现中止折腾了一个多月了,祝你早日能搞定!
si.hStdInput :=hWritePipe;
si.hStdError :=hWritePipe;
unit uConsole;interfaceuses windows, Messages, SysUtils;type
TOnData = procedure(Sender: TObject; Data: String) of object;
TOnRun = procedure(Sender: TObject) of object;
TRedirectedConsole = Class(TObject)
private
fExitTimeOut: Integer;
fStdInRead, fStdInWrite: THandle;
fStdOutRead, fStdOutWrite: THandle;
fStdErrRead, fStdErrWrite: THandle;
fSA: TSecurityAttributes;
fPI: TProcessInformation;
fSI: TStartupInfo;
fDestroying: Boolean;
fCmdLine: String;
fOnStdOut, fOnStdErr: TOnData;
fOnRun, fOnEnd: TOnRun;
fIsRunning: Boolean;
fHidden: boolean;
fMerge: boolean;
fStdOut, fStdErr: String;
function ReadHandle(h: THandle; var s: string): integer;
Procedure EndProcess(hProc: THandle);
protected
public
constructor Create(CommandLine: String);
destructor Destroy; override;
procedure Run;
procedure Stop;
procedure SendData(s: String);
property ExitTimeOut: integer read fExitTimeout write fExitTimeout;
property OnStdOut: TOnData read fOnStdOut write fOnStdOut;
property OnStdErr: TOnData read fOnStdErr write fOnStdErr;
property OnRun: TOnRun read fOnRun write fOnRun;
property OnEnd: TOnRun read fOnEnd write fOnEnd;
property MergeOutput: boolean read fMerge write fMerge;
property IsRunning: boolean read fIsRunning;
property HideWindow: boolean read fHidden write fHidden;
property StdOut: string read fStdOut;
property StdErr: string read fStdErr;
end;implementationconst BufSize = 1024;constructor TRedirectedConsole.Create(CommandLine: String);
begin
inherited Create;
fCmdLine := CommandLine;
fExitTimeOut := 5000;
fIsRunning := False;
fHidden := True;
fMerge := False;
fDestroying := False;
FillChar(fSA, SizeOf(fSA), 0);
fSA.nLength := SizeOf(fSA);
fSA.lpSecurityDescriptor := nil;
fSA.bInheritHandle := True;
CreatePipe(fStdInRead, fStdInWrite, @fSA, BufSize);
CreatePipe(fStdOutRead, fStdOutWrite, @fSA, BufSize);
CreatePipe(fStdErrRead, fStdErrWrite, @fSA, BufSize);
end;destructor TRedirectedConsole.Destroy;
begin
fDestroying := True;
fOnEnd := nil;
fOnRun := nil;
fOnStdOut := nil;
fOnStdErr := nil;
Stop;
CloseHandle(fStdInWrite);
CloseHandle(fStdOutRead);
CloseHandle(fStdErrRead);
inherited;
end;function TRedirectedConsole.ReadHandle(h: THandle; var s: String): integer;
var
BytesWaiting: Cardinal;
Buf: Array[1..BufSize] of char;
{$IFDEF VER100}
BytesRead: Integer;
{$ELSE}
BytesRead: Cardinal;
{$ENDIF}
begin
Result := 0;
PeekNamedPipe(h, nil, 0, nil, @BytesWaiting, nil);
if BytesWaiting > 0 then
begin
if BytesWaiting > BufSize then
BytesWaiting := BufSize;
ReadFile(h, Buf[1], BytesWaiting, BytesRead, nil);
s := Copy(Buf, 1, BytesRead);
Result := BytesRead;
end;
end;
procedure TRedirectedConsole.SendData(s: String);
var
{$IFDEF VER100}
BytesWritten: Integer;
{$ELSE}
BytesWritten: Cardinal;
{$ENDIF}
begin
if fIsRunning then
begin
WriteFile(fStdInWrite, s[1], Length(s), BytesWritten, nil);
end;
end;procedure TRedirectedConsole.Stop;
begin
if fIsRunning then
EndProcess(fPI.hProcess);
end;procedure TRedirectedConsole.EndProcess(hProc: THandle);
var
hLib: THandle;
hRT: THandle;
pExitProcess: pointer;
iTI: Cardinal;
bTerminated: Boolean;
begin
bTerminated := False;
hLib := LoadLibrary('KERNEL32.dll');
if hLib <> 0 then
begin
pExitProcess := GetProcAddress(hLib, 'ExitProcess');
if pExitProcess <> nil then
begin
hRT := CreateRemoteThread(hProc, nil, 0, pExitProcess, nil, 0, iTI); //HERE IS THE GUILTY CODE
if hRT <> 0 then
begin
bTerminated := (WaitForSingleObject(hRT, fExitTimeOut) = WAIT_OBJECT_0);
CloseHandle(hRT);
end;
end;
FreeLibrary(hLib);
end;
if not bTerminated then
begin
TerminateProcess(hProc, 0);
WaitForSingleObject(hProc, fExitTimeOut);
end;
fIsRunning := False;
end;procedure TRedirectedConsole.Run;
var
s: String;
hProcOld: THandle;
BytesWritten:Cardinal;
begin
fStdOut := '';
fStdErr := '';
FillChar(fSI, SizeOf(fSI), 0);
fSI.cb := SizeOf(fSI);
if fHidden then
fSI.wShowWindow := SW_HIDE
else
fSI.wShowWindow := SW_SHOWDEFAULT;
fSI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
fSI.hStdInput := fStdInRead;
fSI.hStdOutput := fStdOutWrite;
if fMerge then
fSI.hStdError := fStdOutWrite
else
fSI.hStdError := fStdErrWrite;
if CreateProcess(nil, PChar(fCmdLine), nil, nil, True, CREATE_NEW_PROCESS_GROUP or NORMAL_PRIORITY_CLASS, nil, nil, fSI, fPI) then
begin
hProcOld := fPI.hProcess;
fIsRunning := True;
CloseHandle(fStdOutWrite);
CloseHandle(fStdErrWrite);
CloseHandle(fStdInRead);
CloseHandle(fPI.hThread);
While ((WaitForSingleObject(fPI.hProcess, 10) = WAIT_TIMEOUT) and fIsRunning) do
begin
if fDestroying then
exit; if ReadHandle(fStdOutRead, s) > 0 then
if Assigned(fOnStdOut) then
fOnStdOut(Self, s)
else
fStdOut := Concat(fStdOut, s);
if ReadHandle(fStdErrRead, s) > 0 then
if Assigned(fOnStdErr) then
fOnStdErr(Self, s)
else
fStdErr := Concat(fStdErr, s);
if Assigned(fOnRun) then
fOnRun(Self);
end; if fDestroying then
exit;
if ReadHandle(fStdOutRead, s) > 0 then
if Assigned(fOnStdOut) then
fOnStdOut(Self, s)
else
fStdOut := Concat(fStdOut, s);
if ReadHandle(fStdErrRead, s) > 0 then
if Assigned(fOnStdErr) then
fOnStdErr(Self, s)
else
fStdErr := Concat(fStdErr, s);
if (fPI.hProcess = hProcOld) then
CloseHandle(fPI.hProcess);
fIsRunning := False;
if Assigned(fOnEnd) then
fOnEnd(Self);
end;
end;end.
var fCon :TRedirectedConsole;
procedure TForm1.Button1Click(Sender: TObject);
begin
fCon := TRedirectedConsole.Create(Edit1.Text);
fCon.OnStdOut := OnConStdOut;
fCon.OnStdErr := OnConStdErr;
fCon.OnRun := OnConRun;
fCon.OnEnd := OnConEnd;
fCon.Run;
end;{
经测试edit1.text := 'd:\delphi\temp\ff\ffmpeg.exe -i d:\abc.mpg -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 d:\abc.flv'注意ffmpge.exe 我并没有带-y 参数。就是说需要确认的,这时候我fCon.SendData('y' + #13#10); //是可以让ffmpge继续运行的。然后fCon.SendData('q' + #13#10); //却不能让 ffmpge正常退出!就是说我CreatePipe了两个管道(一个读一个写)。两次SendData只有发送'y'的时候才成功的。
为什么?区别就在于,前面需要输'y'的时候,ffmpge已经停止了输出信息。其实这时候我SendData('q')也是可以让
ffmpge退出的。问题就在于ffmpge开始转换文件的时候就收不到我的'q'了。
哎~~~看来是ffmpge的问题了。
难道真的要像34楼的方法啊。
procedure TRedirectedConsole.SendData(s: String);
var
{$IFDEF VER100}
BytesWritten: Integer;
{$ELSE}
BytesWritten: Cardinal;
{$ENDIF}
begin
if fIsRunning then
begin
WriteFile(fStdInWrite, s[1], Length(s), BytesWritten, nil);
FlushFileBuffers(fStdInWrite); // add this line
end;
end;
程序执行完这行后,就死了。
特别是楼主帖子的日期,刚好是在我搞delphi ffmpeg之前。
我是2008年3月初开始研究ffmpeg,用了两个月时间,搞定ffmpeg移植为纯delphi控件。
BOOL SendCloseKey()
/*========================================================================
功能:向控制台发送按键消息
说明:模拟按下'q'按键退出FFmpeg控制台程序
----------------------------------------------------------------------------
参数:无
返回值:找不到FFmpeg控制台窗口返回FALSE,发送完毕返回TRUE
==========================================================================*/
{
CString sFileName;
GetExeName(sFileName);
HWND hd=::FindWindow(NULL,sFileName);
if(!hd) return FALSE;
::SendMessage(hd, WM_SYSCOMMAND, SC_HOTKEY, (LPARAM) hd);
char ch='q';
BYTE key=(BYTE)::VkKeyScan(ch);
BYTE ScanCode = LOBYTE(::MapVirtualKey(key, 0));
::keybd_event(key, ScanCode, 0,0);
::keybd_event(key, ScanCode, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,0);
return TRUE;
}