Improve the performance of burning text/ass subtitle streams into the video.
This commit is contained in:
@@ -8,9 +8,11 @@ use App\Libs\Attributes\Route\Get;
|
|||||||
use App\Libs\DataUtil;
|
use App\Libs\DataUtil;
|
||||||
use App\Libs\Enums\Http\Status;
|
use App\Libs\Enums\Http\Status;
|
||||||
use App\Libs\Stream;
|
use App\Libs\Stream;
|
||||||
|
use DateInterval;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use Psr\Http\Message\ResponseInterface as iResponse;
|
use Psr\Http\Message\ResponseInterface as iResponse;
|
||||||
use Psr\Http\Message\ServerRequestInterface as iRequest;
|
use Psr\Http\Message\ServerRequestInterface as iRequest;
|
||||||
|
use Psr\Log\LoggerInterface as iLogger;
|
||||||
use Psr\SimpleCache\CacheInterface as iCache;
|
use Psr\SimpleCache\CacheInterface as iCache;
|
||||||
use Psr\SimpleCache\InvalidArgumentException;
|
use Psr\SimpleCache\InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -26,7 +28,7 @@ readonly class Segments
|
|||||||
'dvd_subtitle',
|
'dvd_subtitle',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(private iCache $cache)
|
public function __construct(private iCache $cache, private iLogger $logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,11 +71,13 @@ readonly class Segments
|
|||||||
|
|
||||||
$incr = 0;
|
$incr = 0;
|
||||||
$subIndex = [];
|
$subIndex = [];
|
||||||
|
$internalSubs = [];
|
||||||
foreach (ag($json, 'streams', []) as $id => $stream) {
|
foreach (ag($json, 'streams', []) as $id => $stream) {
|
||||||
if ('subtitle' !== ag($stream, 'codec_type')) {
|
if ('subtitle' !== ag($stream, 'codec_type')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$subIndex[$id] = $incr;
|
$subIndex[$id] = $incr;
|
||||||
|
$internalSubs[$incr] = $stream;
|
||||||
$incr++;
|
$incr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +239,22 @@ readonly class Segments
|
|||||||
$cmd[] = '-vf';
|
$cmd[] = '-vf';
|
||||||
$cmd[] = "subtitles={$tmpSubFile}" . ($isIntel ? ',format=nv12,hwupload' : '');
|
$cmd[] = "subtitles={$tmpSubFile}" . ($isIntel ? ',format=nv12,hwupload' : '');
|
||||||
} elseif (null !== $subtitle && !$overlay) {
|
} elseif (null !== $subtitle && !$overlay) {
|
||||||
|
$subStreamIndex = (int)$subIndex[$subtitle];
|
||||||
|
$tmpSubFile = r("{path}/t-{name}-internal-sub-{index}.{type}", [
|
||||||
|
'path' => sys_get_temp_dir(),
|
||||||
|
'name' => $token,
|
||||||
|
'index' => $subStreamIndex,
|
||||||
|
'type' => $internalSubs[$subStreamIndex]['codec_name'],
|
||||||
|
]);
|
||||||
|
$streamLink = $this->extractTextSubTitle(
|
||||||
|
$tmpVidFile,
|
||||||
|
$internalSubs[$subStreamIndex]['codec_name'],
|
||||||
|
$subStreamIndex,
|
||||||
|
$tmpSubFile
|
||||||
|
);
|
||||||
|
|
||||||
$cmd[] = '-vf';
|
$cmd[] = '-vf';
|
||||||
$cmd[] = "subtitles={$tmpVidFile}:stream_index=" . (int)$subIndex[$subtitle] . ($isIntel ? ',format=nv12,hwupload' : '');
|
$cmd[] = "subtitles={$streamLink}" . ($isIntel ? ',format=nv12,hwupload' : '');
|
||||||
} else {
|
} else {
|
||||||
$cmd[] = '-sn';
|
$cmd[] = '-sn';
|
||||||
}
|
}
|
||||||
@@ -316,6 +334,58 @@ readonly class Segments
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
private function extractTextSubTitle(string $path, string $type, int $stream, string $cacheFile): string
|
||||||
|
{
|
||||||
|
$cacheKey = md5("{$path}:" . filesize($path) . ":{$stream}");
|
||||||
|
if (null !== ($cached = $this->cache->get($cacheKey, null))) {
|
||||||
|
$stream = Stream::make($cacheFile, 'w');
|
||||||
|
$stream->write($cached);
|
||||||
|
$stream->close();
|
||||||
|
return $cacheFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = [
|
||||||
|
'ffmpeg',
|
||||||
|
'-xerror',
|
||||||
|
'-hide_banner',
|
||||||
|
'-loglevel',
|
||||||
|
'error',
|
||||||
|
'-i',
|
||||||
|
'file:' . $path,
|
||||||
|
'-map',
|
||||||
|
"0:s:{$stream}",
|
||||||
|
'-f',
|
||||||
|
$type,
|
||||||
|
'pipe:1',
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$process = new Process($cmd);
|
||||||
|
$process->setTimeout($stream ? 120 : 60);
|
||||||
|
$process->start();
|
||||||
|
$process->wait();
|
||||||
|
|
||||||
|
if (!$process->isSuccessful()) {
|
||||||
|
$this->logger->error(join(' ', $cmd) . $process->getErrorOutput());
|
||||||
|
return "{$path}:stream_index={$stream}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $process->getOutput();
|
||||||
|
$this->cache->set($cacheKey, $body, new DateInterval('PT1H'));
|
||||||
|
|
||||||
|
$stream = Stream::make($cacheFile, 'w');
|
||||||
|
$stream->write($body);
|
||||||
|
$stream->close();
|
||||||
|
return $cacheFile;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$this->logger->error($e->getMessage(), ['trace' => $e->getTrace()]);
|
||||||
|
return "{$path}:stream_index={$stream}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function getStream(array $streams, int $index): array
|
private function getStream(array $streams, int $index): array
|
||||||
{
|
{
|
||||||
foreach ($streams as $stream) {
|
foreach ($streams as $stream) {
|
||||||
|
|||||||
Reference in New Issue
Block a user