Einzelnen Beitrag anzeigen

Maekkelrajter

Registriert seit: 8. Mär 2017
Ort: Köln
118 Beiträge
 
Delphi 11 Alexandria
 
#1

Externe Programme asynchron trotz WaitForSingleObject?

  Alt 1. Mai 2017, 12:37
Zur Zeit arbeite ich (mal wieder) an einem Programm, das mit iTunes interagiert. Unter anderem enthält es eine Funktion zur Anonymisierung der von Apple gelieferten M4A - Dateien, die ich auf meinen diversen mobilen Geräten abspielen möchte. Diese Dateien enthalten nämlich im Klartext Name und EMail-Adresse und womöglich noch andere, nicht ohne weiteres erkennbare Daten, die auf meine Identität hinweisen. Ich möchte nämlich vermeiden, dass etwa bei Verlust oder Diebstahl eines solchen Gerätes personalisierte Titel aus meinen Mediatheken in irgendwelchen Tauschbörsen auftauchen. Vielleicht alles nur Paranoia, aber das sollte nicht Gegenstand der Diskussion sein

Zur Bearbeitung der Dateien benutze ich das Kommandozeilenprogramm ffmpeg.exe

Delphi-Quellcode:
Function TGLMainform.CMDExec(cmdline:string):Boolean;
var
  i:Integer;
  s1:string;
  Output : TStringList;
  Errors : TStringList;
begin
  result:= false;
  Output := TStringList.Create;
  Errors := TStringList.Create;
  try
    If GetConsoleOutput(cmdline, Output, Errors) then
    begin
      result:= true;
      Loglist.add(#13#10+cmdline);
      Loglist.AddStrings(output);
      for i:= 0 to Errors.count -1 do
      begin
        s1:= Errors[i];
        If pos('WARN',s1) > 0 then
        begin
          Loglist.add('WARNING: ' + s1);
        end;
      end;
    end;
  finally
    Output.free;
    Errors.free;
  end;
end;


const ffmpg:= '"D:\Externals\ffmpeg\ffmpeg.exe" -loglevel error -i ';


procedure TGLMainform.Anonymize(const sourcefile,cleanfile:string);
  var tempfile:string;
begin
  tempfile:= changefileext(cleanfile,'.aac');
  cmdexec(ffmpg + #34 + sourcefile + #34 + ' -acodec copy ' + #34 + tempfile + #34); // Audiostream extrahieren
  cmdexec(ffmpg + #34 + tempfile + #34 + ' -absf aac_adtstoasc -acodec copy ' + #34 + cleanfile + #34); // Audiostream in leeren MP4 Container
  sysutils.DeleteFile(tempfile);
end;
Die Function cmdexec ruft ihrerseits 'GetConsoleOutput' auf und schreibt die Ausgaben und Errors des aufgerufenen Programmes in ein Logfile;
Anschließend werden ausgewählte, zuvor aus dem Original-File ausgelesene und gesicherte Tags in die neue M4A - Datei zurückgeschrieben. Das Ganze läuft in einer Schleife, in der schonmal mehrere 1000 Dateien verarbeitet werden können.
Im Prinzip funktioniert das alles wie es soll.
Gelegentlich kommt es jedoch vor, dass der zweite Aufruf von ffmpeg einen Fehler 'File not found' zurückgibt. Ich nahm zunächst an, dass das temporäre File tempfile.aac zu diesem Zeitpunkt noch nicht erstellt ist. Versuche (auskommentieren des 'sysutils.DeleteFile') ergaben dann aber, dass die Löschung des tempfiles für den Fehler verantwortlich war. WaitForSingleObject signalisiert also offenbar das Beenden von ffmpeg, bevor der jeweilige Kopiervorgang tatsächlich abgeschlossen ist.
Als Workaround habe ich die Namen der Tempfiles zunächst in einer Stringlist gespeichert und erst am Abschluss der ganzen Aktion in einem Rutsch gelöscht. Bisher klappte das immer fehlerfrei, was natürlich keineswegs garantiert ist. Gerade Kopieraktionen im Netzwerk dauern schon mal etwas länger, sodass die Gefahr besteht, dass zumindest der letzte Aufruf von ffmpeg fehlschlägt. Wie kann man also sicherstellen, dass die Kopieraktionen tatsächlich beendet wurden, bevor die Deletefile - Funktion aufgerufen wird?
Mein Ansatz ist, vor dem zweiten Aufruf von ffmpeg bzw. vor dem Aufrufen von DeleteFile

  While not Fileexists(tempfile) do sleep(100)

einzufügen. Das funktioniert natürlich erstmal, aber das kann auch Zufall sein, weil der Fehler sich nicht zuverlässig reproduzieren läßt

Welche Möglichkeiten gibt es sonst noch??
  Mit Zitat antworten Zitat