TProcess.Output/Input (нестандартная консоль)

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Ответить
Voltag
новенький
Сообщения: 29
Зарегистрирован: 02.04.2008 03:49:33

TProcess.Output/Input (нестандартная консоль)

Сообщение Voltag »

Здравствуйте уважаемые!

Пытаюсь работать с ffmpeg.exe и ffplay.exe (http://ffmpeg.org/) цель - захват видео с экрана и потом проиграть. Думал что сделаю быстро(наивный).
Проблема с управлением через TProcess данного exe. Я написал маленькую серию тестов (попробую покороче)(проигрывание файла).
Система: Win10(64) lazarus32 (2.0.10) FPS: 3.2.0

Код: Выделить всё

  MyProc:=TProcess.Create(FormMain);
  MyProc.PipeBufferSize:=1024; //это значение по умолчанию, но я напишу т.к. важно для понимания
  MyProc.Options:= [poUsePipes];
 
  //я исполняю по 1 строчке, но для краткости, отставлю всё здесь
  MyProc.Executable:='calc'; //работает хорошо, как и должно, ничего в консоли не поймали (внизу код) ОК
  MyProc.Executable:='help'; //сработало поймали консоль в мемо, после отработки вышло один раз, прочитали 1024 (смотрите буфер) и вышли - ОК
  MyProc.Executable:='D:\vizion\test\test2.bat'; //это батник с паузами. Проверяю нормально ли работает ожидание, сумели ли словить
  //я подозреваю, что будет работать запись в стрим TProcess.Input (я не проверял, но вроде всё хорошо)
 
  //Внизу дам описание проблем следующего кода
  MyProc.Executable:=path_ffmpeg+'ffplay.exe';
  MyProc.Parameters.Add('D:\vizion\outfolder\output.mkv'); 
  MyProc.Execute();

  Application.ProcessMessages;

  while MyProc.Running do begin
     Label1.Caption:='run';
     Application.ProcessMessages;
     if MyProc.Output.NumBytesAvailable > 0 then
     begin
          MemoCmd.Lines.Add('can be read');
          MemoCmd.Lines.LoadFromStream(MyProc.Output);
     end;
  end;
  Label1.Caption:='not run';

  MyProc.Terminate(0);
  MyProc.Free;
                                               


Подаём экзешник с полным путём (ffplay.exe) и параметр к нему. Всё правильно исходи и из документации, и руками, когда я работаю с консолью всё хорошо
Но при указании [poUsePipes] я не получаю данных (код внизу) ни единого байтика, и само проигрывание не работает (чёрный экран консоли).
Execute без [poUsePipes] работает, а именно, вызывается консоль туда пишутся данные, открывается окно с проигрывателем и файл проигрывается.
ВАЖНО: Консоль там не простая, там отображено динамически данные типа FPS в реальном времени. Возможно поэтому захват консоли не работает, я не имел с таким дело.
При ошибках (например файла не существует) пайп работает как и должно, т.е. я могу всё уловить.
Примечание: Если Tprocess или TUTFProcess на форме как компоненты, т.е. они статические
то при вызове повторно, окошко с видео появляется, т.е. я один раз клацаю на кнопку, второй раз. Потом закрываю первое висящую консоль, и появляется проигрыватель(играет видео), вторая консоль видна, но данные я не могу с неё получить. Я проверил все настройки, но не понимаю почему так.
И последнее, проблема в том что мне надо прервать запись/проигрывание, для этого я должен в консоль написать пару чаров(насколько я понимаю), но,
1. Я не получил первичный ответ от процесса, может он занят, не знаю я
2. При попытки записи нет эффекта или ошибка памяти...
Вот ответ по поводу работы с ffmpeg, я могу плохо объяснять
https://stackoverflow.com/questions/797 ... eo-capture

Возможно, я должен был написать всё раньше т.к. плохо биться долго головой о стену.
Да, я мог бы скачать длл ки, и заголовочные, но я думал что через консоль то проще. + нет зависимости о заголовков + документация для EXE для всех языков с примерами + могу написать хоть автоматическое обновление
Пожалуйста подскажите, в чём может быть проблема.

upd:
пытался читать "нормальным методом" для больших данных в консоли, через TTimer(1000)
висит намертво вот на этой строке

Код: Выделить всё

  BytesRead    : longint;
  Buffer         : array[1..BUF_SIZE] of byte;
...
BytesRead := MyGlobalProc.Output.Read(Buffer, BUF_SIZE);

Величина буфера совпадает с PipeBufferSize
Поигрался с выводом логов. Нашёл настройку ffmpeg вывод статичных логов, читать их не получается (после любых операций остаётся 2 байта на чтение), хотя в консоли красным по чёрному написана ошибка =)))

upd:
Можно настроить вывод ffmpeg более дружелюбнее, я перенаправил весь вывод в файл
можно это делать как стандартными способами операционных систем(наверно), так и сам ffmpeg имет систему репортов, необходимо указать параметр в Environment (TProcess)
К примеру:

Код: Выделить всё

  MyGlobalProc.Environment.Add('FFREPORT=file='''+path_ffmpeg+'fflog.log'':level=24');  //24 - уровень лога в файл

А вывод логов на уровне консоли отключить:

Код: Выделить всё

ffplay.exe output.mkv -loglevel +quiet

позволит не получать ничего в консоль и процесс будет как бы висеть, если всё хорошо
зато можно работать по привычному, прямо с файлом. Хотя, не хорошо... лучше бы работать с памятью и не трогать диск

upd:
Стена оказалась сильнее =))). Я всё сделал через консоль, но при указании [poNoConsole] в TProcess я не могу корректно закрыть консольное приложение. on_close/exit не отрабатывается у процесса без консоли, например надо закончить захват экрана, и при выходе ffmpeg(в onclose) завершает концовку файла и закрывает медиа контейнер.
Terminate пробовал , пробовал PostMessage - говорит что WM_CLOSE для окон, хотя бы консольных. Пробовал через taskkill, не хочет работать без ключа /f который принудительно kill приложение.
[poNoConsole] - это WinApi CreateProcess с параметром CREATE_NO_WINDOW (как то так). А начиналось всё не так плохо...
Последний раз редактировалось Voltag 27.07.2021 02:21:41, всего редактировалось 2 раза.
Аватара пользователя
zoltanleo
постоялец
Сообщения: 459
Зарегистрирован: 17.10.2013 10:55:01

Сообщение zoltanleo »

Написано очень много. Хорошо бы суть вопроса излагать в 2-3 строках. А подробности отдельно :)

Если посмотреть на код, то ты просто плохо изучил документацию. Вкратце - на каждый исполняемый файл -один TProcess. Либо запускаешь экзешники последовательно. Пока могу только отравить к документации, особенно обрати на комментарий переводчика
https://wiki.freepascal.org/Executing_E ... 0.B4.D0.B0

У меня все робит, консольный вывод идет в TMemo на форме
Voltag
новенький
Сообщения: 29
Зарегистрирован: 02.04.2008 03:49:33

Сообщение Voltag »

to zoltanleo
Если я запускаю bat, например с последовательным выводом, то всё нормально работает.
Проблема в том, что при вызове определённых программ (ffmpeg.exe ffplay.exe) по какой-то причине консольный вывод у них не ловиться, происходит ошибка на каком-то уровне.
Некоторые процессы вывода в консоль этих файлов идут с динамичеки/интерактивно 1 строкой, в которой всё меняется (как раз я не могу получить текст из консоли).
Представьте себе архив который распоковываеться в консоли и 1 строкой пишет %(от 1-100) распаковки, (1 строкой - это не переходя на новую строку). В 1 строке меняются значения от 1 до 100.
И в процессе я не могу прочитать данные.
Это отдельный вопрос, как такое сделать и главный вопрос можно ли такое читать и как.
Спасибо за ответ =)

Я стараюсь не только получить ответ, но и чтобы проблема была решена, чтобы было видно как она может быть решена, а если нет, то почему.
На нестандартные вещи уходит очень много времени. И я очень радуюсь, когда нахожу решения, которые были оставлены другими людьми.

p/s подправил заголовок темы, теперь он более понятен
Аватара пользователя
zoltanleo
постоялец
Сообщения: 459
Зарегистрирован: 17.10.2013 10:55:01

Сообщение zoltanleo »

TProcess - это одноразовое действие исполняемого файла. Боюсь, что ни о какой интерактивности речи быть не может. Батник - это вообще запуск системного шелла. И немного из другой области.

На мой взгляд, наверное стоит как-то пересмотреть способы решения задач.
Аватара пользователя
Снег Север
долгожитель
Сообщения: 3067
Зарегистрирован: 27.11.2007 15:14:47
Контактная информация:

Сообщение Снег Север »

В делфи (под виндой, естественно) через использование функций API можно полностью перехватить и ввод, и вывод в запущенную из приложения стороннюю консольную программу. Через named pipes. Когда-то делал сам. Готовое решение несложно отыскать через гугл. Можно ли сделать подобное с TProcess не знаю.
Voltag
новенький
Сообщения: 29
Зарегистрирован: 02.04.2008 03:49:33

Сообщение Voltag »

to zoltanleo
Да, я согласен с вами, что часто приходятся выполнять задачи одноразовый запуска, TProcess c этим справляется. TProcess есть так-же как визуальный компонент, я предполагаю, что его делали для многоразового использования, иначе он просто не нужен в панели. Так-же у TProcess есть задокументированный TProcess.Input (stream) который позволяет писать в консоль. Т.е. он подразумевает так-же интерактивность. И с этим тоже нет проблем. Если пользоваться стандартным выводом построчным, то всё хорошо(я не проверял прям основательно). Но, вывод файлов не стандартный, потому, что он не читается стандартными средствами, или я что-то пропускаю или не вижу строку в документации. Я решил эту проблему комбинацией логов самого ffmpeg и некоторой доступной логикой того же TProcess.

Т.е я решил проблему, сделал это с "другой" стороны. Но я нашёл ещё одну проблему, которую описал. О том, что флаг poNoConsole, не скрывает консоль так, чтобы её не было видно, оказалось что она лишает процесса "оконности", например ему нельзя послать postMessage потому, что "Ошибка: неверный дискрптор окна". Хотя, я считал наивно, что любой процесс - это окно. Видимо и очевидно, что я плохо знаю архитектуру ОС. и я запутался =)))

Добавлено спустя 11 минут 32 секунды:
to Снег Север
Спасибо за совет! Я взял TProcess как задел на кроссплатформенность, сижу экспериментирую. Стали понятны некоторые ограничения. Пытаюсь использовать систему сообщений виндовых, что как бы убивает всю идею, но по другому видимо никак. Если сдамся совсем, то перейду на винапи.
Аватара пользователя
zoltanleo
постоялец
Сообщения: 459
Зарегистрирован: 17.10.2013 10:55:01

Сообщение zoltanleo »

Консольные окна не имеют хендла, если я правильно ошибаюсь. А значит никаких сообщений.

А TProcess - кроссплатформенный шелл. Если исходить из этих предпосылок, то все становится на свои места
Voltag
новенький
Сообщения: 29
Зарегистрирован: 02.04.2008 03:49:33

Сообщение Voltag »

да, zoltanleo, вы правы. цитирую справку винды:
CREATE_NO_WINDOW
The process is a console application that is being run without a console window. Therefore, the console handle for the application is not set.
т.е. Консольное приложение запускается без окна консоли. Следовательно, хендл консоли для приложения не установлен.
TProcess вызывает CreatProcessW (я смотрел TProcessUTF8). Мне понятно почему меня не пускает, хотя on_close/exit есть.

не стал перфектничать (работа без консоли). Вместо [poNoConsole] сделал:

Код: Выделить всё

TProcess.ShowWindow := swoHide;

условно решил проблему, хотя в процессах виден "хост окна консоли"(win10), зато не буду голову ломать никому.

Спасибо всем за ответы.
Аватара пользователя
Ichthyander
энтузиаст
Сообщения: 701
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань
Контактная информация:

Сообщение Ichthyander »

Много написано, все не осилю. Но смысл как я понял связь с приложением через cli интерфейс. Вот пример либы, которая подключается к внешнему приложению через консоль, отправляет команды и считывают информацию из него. Вообщем, полная интерактивность, работает через TProcess: https://github.com/Al-Muhandis/fp-youtube-dl
Ответить