php - 为什么 PHP 在向以 proc_open 启动的进程写入 4096 字节后会挂起?

标签 php stdin vlc proc-open

对于任何想知道的人来说,在将其全部放置几个小时后,它现在可以完美运行了。

我正在尝试使用 PHP 将视频文件传递给 VLC,作为某人即将进行的项目提案的概念证明。

我已经通过创建一个小于 4KB 的文件(灰色,持续 10 秒)并测试我的脚本来证明它是有效的,但我很好奇首先发生这种情况的原因。

这里有一个示例脚本来理解我的意思:

$filepath = 'Path/to/your/video';
$vlcpath = 'Path/to/your/VLC executable';

$descriptorspec = array(
    0 => array("pipe", "r"),  // stdin
    1 => array("pipe", "w"),  // stdout
    2 => array("pipe", "w")   // stderr
);

$vlc = proc_open($vlcpath . ' -', $descriptorspec, $pipes, null, null, ['bypass_shell' => true]);

$file = fopen($filepath, 'r');
stream_copy_to_stream($file, $pipes[0]);
fclose($file);
proc_close($vlc);

我在 Windows 10 上使用 PHP 5.5.31。我在 PHP 站点上看到了一些关于这种事情的错误报告,但他们建议最新版本已经修复了它。我不太了解阻止流的概念,但我已经尝试过 PHP v7.0.3 无济于事。

我正在使用命令行运行此脚本:php file.php

最佳答案

我尝试在 Windows 上使用 LAME 将 WAV 转换为 MP3 时遇到了完全相同的问题,但无法找到可行的解决方案。

我尝试了很多方法,包括阻塞/非阻塞写入、写入小块(< 1k)数据、休眠和尝试写入,但它永远无法写入所有数据。在它失败之前我能写的大约是 40kb(fwrite 失败总是返回 0 并且永远不会向流写入更多数据,无论我等待多长时间;无论之前写入的 block 的大小如何。我什至尝试在写入之间等待几秒钟,他们总是会成功达到大约 30-40kb 并且再也不会写入更多)。

最终我放弃了,幸运的是 LAME 可以从文件而不是 STDIN 读取输入,所以我只是选择将数据写入临时文件,调用 LAME,然后删除临时文件。

相关代码如下:

// 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-blocking
for ($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 Windows
if ($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;

关于php - 为什么 PHP 在向以 proc_open 启动的进程写入 4096 字节后会挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35682996/

相关文章:

php - 查询结果到对象集合

cocoa - 如何在 MacRuby 中执行命令、向其标准输入提供数据并从其标准输出读取数据?

c - 当我有类似 "executable < example_file"或“cat xyz | 可执行文件?”时如何检查不正确的标准输入?

python - 如何写入 Python 子进程的标准输入?

java - vlcj 全屏视频播放器

git - 无法从 git.videolan.org 运行 git clone

php - 如何为用 PHP 生成的图像设置默认名称 'save-as'?

php - 将多个图像字段上传到服务器mysql中的每一行

javascript - onResize() 在 WordPress 4.7.2 中不起作用

Xcode 4.4.1 中 non_lazy_ptr 的 iOS SDK 5.1 链接器错误