弄了三天都没有弄成功过。。就是用线程启动一个外部程序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.

解决方案 »

  1.   

    如果把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;
    有什么办法可以解决这个问题。。
      

  2.   

    这个方法好像不行。。不知道那里有问题大家帮我看看。。问题出在那里
    可以用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就算把线程关了外部程序能正常的结束吗?
    有谁能告诉我帮我想个办法。。
      

  3.   

    能处理消息。。这个程序在转的时候只要按下Q和 ctrl+c就能停止转换。。而且转换出来的视频是可以放的。。
    如果直接把进程关了。那么转换出来的视频就不能播放我是用线程调用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他能停止谁能帮我看看。。已经一个多星期过去了。问题还是没有解决。。高手。。帮帮我吧。。
      

  4.   

    FFMPEG.EXE 程序找不到windows版,能不能提供一个,我来试试.
      

  5.   

    UP  如果知道怎么解决的可以加我QQ165067375
      

  6.   

    我这个调用一个外部视频转换器。。就是说这个外部程序在转换当中不能直接关掉。也就是说不能直接关进程。必需要按Q或CTRL+C这个命令停止这样转换一半的视频就可以播放。突然中断这样视频转换到中途的视频就不能放。这个外部程序就跟CMD差不多要用命令来转。比如:FFMPEG.EXE   -y   -c   ssdcwt.avi   //这是参数 
    现在的问题是我把这个外部程序的输入信息读到Memo1里面去了。这个外部程序就是一个窗口里面什么东西都没有都读到MEMO1里面来了这个如果我给这个外部程序发消息Q没有反应。。如果我没有把外部程序的信息读到MEMO1里面去。直接在窗口上显示这样我发送消息Q他就能自动关闭。而且转换到一半就停止的视频可以播器。 如果把外部信息都读到MEMO1里面去了。发消息给外部程序没有一点反应所以问题就在这里。我把外部程序返回的信息读到MOME1里面去有什么办法可以让他在转换当中正常停。也就是发送消息。能让他停止也不是直接关进程。。 
    我说的够清楚了吧。你可以试一下我的代码
      

  7.   

    我这个调用一个外部视频转换器。。就是说这个外部程序在转换当中不能直接关掉。也就是说不能直接关进程。必需要按Q或CTRL+C这个命令停止这样转换一半的视频就可以播放。突然中断这样视频转换到中途的视频就不能放。这个外部程序就跟CMD差不多要用命令来转。比如:FFMPEG.EXE   -y   -c   ssdcwt.avi   //这是参数 
    现在的问题是我把这个外部程序的输入信息读到Memo1里面去了。这个外部程序就是一个窗口里面什么东西都没有都读到MEMO1里面来了这个如果我给这个外部程序发消息Q没有反应。。如果我没有把外部程序的信息读到MEMO1里面去。直接在窗口上显示这样我发送消息Q他就能自动关闭。而且转换到一半就停止的视频可以播器。 如果把外部信息都读到MEMO1里面去了。发消息给外部程序没有一点反应所以问题就在这里。我把外部程序返回的信息读到MOME1里面去有什么办法可以让他在转换当中正常停。也就是发送消息。能让他停止也不是直接关进程。。 
    我说的够清楚了吧。你可以试一下我的代码
      

  8.   

    WriteFile(hWritePipe, 'q', 2, &dwBytes, NULL);
      

  9.   

    谁能帮我看看。。这个方法那里出现了问题。不成功//WriteFile(hWritePipe, 'q', 2, &dwBytes, NULL); 
      

  10.   

    谁能帮我看看。。这个方法那里出现了问题。不成功//WriteFile(hWritePipe, 'q', 2, &dwBytes, NULL); 
      

  11.   

    控制外部程序很复杂,我不想也没有测试,但用PIPE或PostThreadMessage应该可以。如果确实不行,可以考虑这样:下载FFMPEG的代码,修改 ffmpeg.c1  把 main 改名,例如 ffmpeg_main(int argc, char **argv)
    2  重新设计 decode_interrupt_cb(),通过判断一个全局变量 ffmpeg_ctrl,实现中止
    3  设计一个函数 ffmpeg_quit(),设置全局变量 ffmpeg_ctrl
    4  编译成 DLL,导出 ffmpeg_main 和 ffmpeg_quit
    5  通过调用这两个函数实现启动和停止(要在两个不同的线程中调用)也可以不用全局变量,用全局EVENT等实现中止折腾了一个多月了,祝你早日能搞定!
      

  12.   

    可能还要修改一些内容,例如不要read_key函数等,自己尝试一下,就算不成功也能学到很多东西:)
      

  13.   

    用PIPE失败,可能问题出在这里:si.hStdOutput :=hWritePipe;
    si.hStdInput :=hWritePipe;
    si.hStdError :=hWritePipe;
      

  14.   

    //网上找到的一个控制控制台的.pas
    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.
      

  15.   

    //调用
    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楼的方法啊。
      

  16.   

    试一试
    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; 
      

  17.   

    FlushFileBuffers(fStdInWrite); // add this line 
    程序执行完这行后,就死了。
      

  18.   

    FlushFileBuffers虽然可以保证在文件关闭之前我的WriteFile数据被写入磁盘,但是管道并没有关闭啊,文件应该也没有关闭。
      

  19.   

    无意中搜到楼主这个帖子,看了一下,楼主还真是执着。
    特别是楼主帖子的日期,刚好是在我搞delphi ffmpeg之前。
    我是2008年3月初开始研究ffmpeg,用了两个月时间,搞定ffmpeg移植为纯delphi控件。
      

  20.   

    贴段之前弄的c++代码,delphi的话call sendkey那个unit可以实现相同效果.
    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;
    }