Why does PHP hang after writing 4096 bytes to a process started with proc_open?
I ran into the exact same issue trying to do WAV to MP3 conversion using LAME on Windows and was unable to find a workable solution.
I tried dozens of things including blocking/non-blocking writes, writing small (< 1k) chunks of data, sleeping and trying to write but it never was able to write all data. About as much as I could ever write before it failing was around 40kb (failure being fwrite would always return 0 and never write more data to the stream, no matter how long I waited; regardless of the sizes of the chunks written before. I even tried waiting seconds between writes and they would always succeed to about 30-40kb and never write more).
Ultimately I gave up and luckily LAME could read input from a file instead of STDIN, so I just opted to write the data to a temp file, call LAME, and remove the temp file.
Here's the relevant code:
// file descriptors for reading and writing to the Lame process$descriptors = array( 0 => array('pipe', 'r'), // stdin 1 => array('pipe', 'w'), // stdout 2 => array('pipe', 'a'), // stderr);if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { // workaround for Windows conversion // writing to STDIN seems to hang indefinitely after writing approximately 0xC400 bytes $wavinput = tempnam(sys_get_temp_dir(), 'wav'); if (!$wavinput) { throw new Exception('Failed to create temporary file for WAV to MP3 conversion'); } file_put_contents($wavinput, $data); $size = 0;} else { $wavinput = '-'; // stdin}// Mono, variable bit rate, 32 kHz sampling rate, read WAV from stdin, write MP3 to stdout$cmd = sprintf("%s -m m -v -b 32 %s -", self::$lame_binary_path, $wavinput);$proc = proc_open($cmd, $descriptors, $pipes);if (!is_resource($proc)) { throw new Exception('Failed to open process for MP3 encoding');}stream_set_blocking($pipes[0], 0); // set stdin to be non-blockingfor ($written = 0; $written < $size; $written += $len) { // write to stdin until all WAV data is written $len = fwrite($pipes[0], substr($data, $written, 0x20000)); if ($len === 0) { // fwrite wrote no data, make sure process is still alive, otherwise wait for it to process $status = proc_get_status($proc); if ($status['running'] === false) break; usleep(25000); } else if ($written < $size) { // couldn't write all data, small pause and try again usleep(10000); } else if ($len === false) { // fwrite failed, should not happen break; }}fclose($pipes[0]);$data = stream_get_contents($pipes[1]);$err = trim(stream_get_contents($pipes[2]));fclose($pipes[1]);fclose($pipes[2]);$return = proc_close($proc);if ($wavinput != '-') unlink($wavinput); // delete temp file on Windowsif ($return !== 0) { throw new Exception("Failed to convert WAV to MP3. Shell returned ({$return}): {$err}");} else if ($written < $size) { throw new Exception('Failed to convert WAV to MP3. Failed to write all data to encoder');}return $data;