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

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.: в процессах не силён, поэтому просьба сильно не пинать.
 

Alexandre

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

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

все просто и ясно как чистый перец...
 

MVH

Новичок
Alexandre, а что будет что, если видео удалить в процессе перекодирования? Не хотелось бы запрещать удаление видео, если перекодирование началось.
 

findnext

Новичок
MVH
так перед началом ставь флаг что перекодирование началось
 

MVH

Новичок
После "fwrite($pipes[0], "ffmpeg -y -i $from $to"); " скрипт блокируется оказывается :(

-~{}~ 05.08.09 00:30:

findnext, да нет, я просто не хоту лишать пользователя возможности удаления видео, если оно перекодируется. Так с этим другие заморочки ещё связаны. Если можно будет сделать то, что я хочу, то мне проще будет.
 

findnext

Новичок
MVH
у тебя пользователь вручную удаляет? флаг как раз и должен сказать пользователю, что видео невозможно удалить, так как перекодируется.
 

MVH

Новичок
findnext, давайте не отходить от темы. Я хочу сделать, что бы можно было удалять видео, даже, если прекодирование началось. У меня у видео файла может перекодироваться на основе него несколько доп. файлов.
 

dimagolov

Новичок
MVH, тебе надо иметь возможность удалить видео или прибить процесс ffmpeg? если первое, то просто прибивай, ffmpeg отработает без проблем, для него файл будет доступен (ну или если он с дуру его переоткрывает в процессе, что вряд ли, то он слетит с ошибкой).
если надо прибивать процесс, то тут будет сложнее...
 

Alexandre

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

Не хотелось бы запрещать удаление видео, если перекодирование началось
ненадо этого хотеть
 

MVH

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

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

-~{}~ 05.08.09 19:01:

Да, спасибо всем за участие!
 

FractalizeR

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

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

Alexandre

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

MVH

Новичок
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'ами. А ведь так наиболее оперативнее можно было бы сделать перекодирование...
 

Alexandre

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