Неблокирующее чтение процесса с возможностью посыла команд

  • Автор темы Автор темы MVH
  • Дата начала Дата начала

MVH

Новичок
Неблокирующее чтение процесса с возможностью посыла команд

Помогите, надо из php в Fedora вызвать через шелл программу для перекодирования видео (ffmpeg), получать от неё данные в цикле и при необходимости прервать её выполнение. Ограничений в хостинге нет.

Пробовал с proc_open - не получается:

PHP:
$descriptorspec = array(
	0 => array("pipe", "r"),  // stdin это канал, из которого процесс будет читать
	1 => array("pipe", "w"),  // stdout это канал, в который процесс будет записывать
	2 => array("pipe", "w"), // stderr это файл для записи ошибок процессом
);

$process = proc_open('/bin/sh', $descriptorspec, $pipes);

if (is_resource($process))
{
	stream_set_blocking($pipes[1], false);
	stream_set_blocking($pipes[2], false);
	
	$from = escapeshellarg('video.mov');
	$to = escapeshellarg('video.wmv');

	//загвоздка в том, что эта команда начнёт выполняться только после fclose($pipes[0]), но тогда я не смогу послать ей другую команду
	fwrite($pipes[0], "ffmpeg -y -i $from $to");
	
	
	
	while (true)
	{
		if (какое-то действие)
		{
			fwrite($pipes[0], "q");
			break;
		}
	}
    
	fclose($pipes[0]);
	fclose($pipes[1]);
	fclose($pipes[2]);
	proc_close($process);
}

P.S.: в процессах не силён, поэтому просьба сильно не пинать.
 
Помогите, надо из php в Fedora вызвать через шелл программу для перекодирования видео (ffmpeg), получать от неё данные в цикле и при необходимости прервать её выполнение.
а зачем такие сложности???

я делаю так:
пишу данные видео в очередь
на фронтэнде отображаю "видео в обработке"
по крону запускаю скрипт обработчик (тот же popen) ... есть вариант, чтоб запустить в бэдграунде через fork, но через крон проще
как только обработка заканчивается, ставлю флаг "Обработан"
AJAX-сом тягаю файл "статуса процесса"
как только появляется статус ОК - вывешиваю превьюшку видео

все просто и ясно как чистый перец...
 
Alexandre, а что будет что, если видео удалить в процессе перекодирования? Не хотелось бы запрещать удаление видео, если перекодирование началось.
 
MVH
так перед началом ставь флаг что перекодирование началось
 
После "fwrite($pipes[0], "ffmpeg -y -i $from $to"); " скрипт блокируется оказывается :(

-~{}~ 05.08.09 00:30:

findnext, да нет, я просто не хоту лишать пользователя возможности удаления видео, если оно перекодируется. Так с этим другие заморочки ещё связаны. Если можно будет сделать то, что я хочу, то мне проще будет.
 
MVH
у тебя пользователь вручную удаляет? флаг как раз и должен сказать пользователю, что видео невозможно удалить, так как перекодируется.
 
findnext, давайте не отходить от темы. Я хочу сделать, что бы можно было удалять видео, даже, если прекодирование началось. У меня у видео файла может перекодироваться на основе него несколько доп. файлов.
 
MVH, тебе надо иметь возможность удалить видео или прибить процесс ffmpeg? если первое, то просто прибивай, ffmpeg отработает без проблем, для него файл будет доступен (ну или если он с дуру его переоткрывает в процессе, что вряд ли, то он слетит с ошибкой).
если надо прибивать процесс, то тут будет сложнее...
 
а что будет что, если видео удалить в процессе перекодирования?
а как ты его удалишь???
во первых идет блокировка обрабатываемого исходного файла утилитой ffmpeg
во вторых - работаем с tempname
в третьих - не разрешаем пользователю действий с файлом со статусом "КОНВЕРТ"
в четвертых - используем оконечные имена типа md5( user_id+time() ), которые заносятся в БД уже после конвертации

Не хотелось бы запрещать удаление видео, если перекодирование началось
ненадо этого хотеть
 
Alexandre, попробовал, файл исходный во время кодировки удаляется, хотя ffmpeg продолжал читать их открытого потока до конца. На блокировку, как я понимаю, внимания можно не обращать и смело удалять файл.

Короче, решения нормального не нашёл и уже заколебался. Плюнул на удобство и оставил как есть (запрет изменений и удаления).

-~{}~ 05.08.09 19:01:

Да, спасибо всем за участие!
 
Автор оригинала: MVH
Alexandre, попробовал, файл исходный во время кодировки удаляется, хотя ffmpeg продолжал читать их открытого потока до конца. На блокировку, как я понимаю, внимания можно не обращать и смело удалять файл.
Когда вы вызываете unlink() на файле в Linux, а в это время он открыт в другой программе, он пропадет из каталога, но физически не удалится и все еще будет занимать место и будет доступен открывшей его программе полностью. Он будет физически удален только после закрытия всеми использующими его программами. Это коренным образом отличается от удаления файлов в Windows.

По теме. А нельзя немного наоборот работу построить? Запускать ffmpeg по exec() чтобы не блокировать работу скрипта, передавать ей параметры в командной строке и просить писать результат в файл. Получать id ее процесса (скажем, через ps). Прерывать по kill -9 если что.
 
По теме. А нельзя немного наоборот работу построить? Запускать ffmpeg по exec() чтобы не блокировать работу скрипта,
хотел сказать через крон, через вызов exec из скрипта WEBApp никак не получился. Это я предложил в самом начале, да и вообще проблема выеденного яйца не стоит.
 
FractalizeR, спасибо за разъяснение.

Я сейчас сделал так:
1. вызываю по крону вызываю скрипт (например, каджые 5 минут)
2. смотрю сколько уже в данный момент перекодируется файлов (запрос в базу count(*), где статус="идёт перекодировка") и, если слишком много, то exit из скрипта.
3. если всё нормально, то запускаю бесконечный цикл
4. на каждой итерации выбираю из базы файл (запись о файле) требующем перекодировку
5. у записи ставлю статус "идёт перекодировка" (изменение, но не удаление файла запрещается при этом), сохраняю id записи о файле (attachment__id)
6. если статус не установился (кто-то уже начал перекодировать, то пропускает данный файл)
7. начинаю перекодировать скрипт. Вот здесь я хотел неблокирующий proc_open и в бесконечном цикле читать данные ffmpeg и периодически делать запрос в базу в целью проверить существования записи attachment__id и, если записи не окажется (равносильно удалению файла), посылать ffmpeg'у в stdin команду о завершении и удалять перекодированный не до конца файл.

8. по окончанию перекодировки заменяю файл новый, перекодированным (или ничего не заменяю, если создаётся доп. файл из родительского - это у меня в CMS так нада), обновляю запись в базе
9. проверяю, есть ли в базе запись с attachment__id и, если нет, значит за время перекодировки файл был удалён и удаляю созданные только что файлы. Проблема может быть только в том, что если удалить много перекодирующихся файлов, то ffmpeg-зомби будут дорабатывать своё, но это, думаю, ничего страшного (в моём случае).
10. зарегистрировать shutdown функцию, в которой дождаться перекодирования файла (ведь завершение работы php скрипта не убивает же порождённые в нём через exec() процессы?) и сделать шаги 8, 9 и 10.


мне нужно было 2 вещи:
1. в случае удаления файла убивать процесс ffmpeg, но на это я пока забил, т.к. мне не очень страшно
2. отображать прогресс перекодирования (сколько процентов перекодирования выполнено)
это я сейчас делаю через неблокирующий exec, перенаправляя вывод в файл:
exec("ffmpeg -i qwe.avi qwe.flv 2>output_file.txt");
и в бесконечном цикле читаю файл output_file.txt, смоютрю как он наполняется, а пользователь видит прогресс :)

Так что в принципе, всё решилось вроде.



Но остался чисто теоретический вопрос: возможен ли неблокирующий proc_open?
Очень удобно было бы выполнить proc_open процесса и дальше, в бесконечном цикле, читать stdout и stderr и при необходимости посылать команду в stdin.
Т.е. сделать то же, что происходит, когда через putty коннектишься, делашь ping ya.ru и тебе будет идти всё время пинг, пока Ctrl+C не нашжёшь, послав соответствующий символ прерывания команды.

-~{}~ 07.08.09 16:52:

Alexandre, а я сначала не понял вашей идеи в fork'ами. А ведь так наиболее оперативнее можно было бы сделать перекодирование...
 
а я сначала не понял вашей идеи в fork'ами. А ведь так наиболее оперативнее можно было бы сделать
перекодирование..по крону запускаю скрипт обработчик (тот же popen) ... есть вариант, чтоб запустить в бэдграунде через fork, но через крон проще
пишется программа на Си в 5 строчек, которая forkает себя : гл процесс возвращает Ok, а дочерний запускает по system('/usr/local/bin/php /full/path/to/script.php ');
При желании могу выложить код, если решишься на это.
данную консольную прогу вызываешь system('/path/to/proga/bground'); //bground - имя программы
можно через argv передавать имя php скрипта
активно использую данную технологию совместно с аяксом для тяжелых скриптов, перекодировок и прочей ерунды.
 
Сверху