mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-31 11:03:22 +01:00
fix(stream): keep an idle on-demand stream's stream_info
An adaptive master lists a variant only by the resolution and bitrate in its stream_info, and stopping an on-demand stream for having no viewers cleared it: an idle HD/FHD variant was left out of the master, and nothing could then start it, so players only ever got the variants already running. The two idle stops (OndemandCommand, the on-demand stop in cron:streams via StreamProcess::stopStream(..., idle: true)) now keep stream_info; a manual stop, a restart and every other stop still clear it. Refs: #61
This commit is contained in:
@@ -193,7 +193,8 @@ class OndemandCommand implements CommandInterface {
|
||||
@unlink($queueFile);
|
||||
@unlink(SIGNALS_TMP_PATH . 'admin_' . $rStreamID);
|
||||
|
||||
StreamStateWriter::update(intval($rStreamID), intval(SERVER_ID), ['bitrate' => null, 'current_source' => null, 'to_analyze' => 0, 'pid' => null, 'stream_started' => null, 'stream_info' => null, 'audio_codec' => null, 'video_codec' => null, 'resolution' => null, 'compatible' => 0, 'stream_status' => 0, 'monitor_pid' => null], $db);
|
||||
// stream_info is kept: an adaptive master lists the idle variant by its last probe.
|
||||
StreamStateWriter::update(intval($rStreamID), intval(SERVER_ID), ['bitrate' => null, 'current_source' => null, 'to_analyze' => 0, 'pid' => null, 'stream_started' => null, 'audio_codec' => null, 'video_codec' => null, 'resolution' => null, 'compatible' => 0, 'stream_status' => 0, 'monitor_pid' => null], $db);
|
||||
|
||||
// MAIN refreshes its cache from the node's events once its store keeps the streams.
|
||||
if (!$rLocal) {
|
||||
|
||||
@@ -241,7 +241,7 @@ class StreamsCronJob implements CommandInterface {
|
||||
}
|
||||
if ($rQueue == 0 && $rAdminQueue == 0 && $rStream['online_clients'] == 0 && (file_exists(STREAMS_PATH . $rStream['stream_id'] . '_.m3u8') || SettingsManager::getInt('on_demand_wait_time') < time() - intval($rStream['stream_started']) || $rStream['stream_status'] == 1)) {
|
||||
echo 'Stop on-demand stream...' . "\n\n";
|
||||
StreamProcess::stopStream($rStream['stream_id'], true);
|
||||
StreamProcess::stopStream($rStream['stream_id'], true, true);
|
||||
// Stopped: nothing below applies (it would start a thumbnail
|
||||
// and a TV archive worker for the stream just stopped).
|
||||
continue;
|
||||
|
||||
@@ -850,13 +850,18 @@ class StreamProcess {
|
||||
*
|
||||
* @param int $rStreamID Stream id.
|
||||
* @param bool $rWithMonitor Also clear monitor_pid (full stop vs. movie stop).
|
||||
* @param bool $rKeepInfo Keep stream_info: an idle on-demand stream's last
|
||||
* probe is what an adaptive master lists it by (issue #61).
|
||||
* @return void
|
||||
*/
|
||||
private static function resetStreamServerRow(int $rStreamID, bool $rWithMonitor = false) {
|
||||
private static function resetStreamServerRow(int $rStreamID, bool $rWithMonitor = false, bool $rKeepInfo = false) {
|
||||
$rFields = ['bitrate' => null, 'current_source' => null, 'to_analyze' => 0, 'pid' => null, 'stream_started' => null, 'stream_info' => null, 'audio_codec' => null, 'video_codec' => null, 'resolution' => null, 'compatible' => 0, 'stream_status' => 0];
|
||||
if ($rWithMonitor) {
|
||||
$rFields['monitor_pid'] = null;
|
||||
}
|
||||
if ($rKeepInfo) {
|
||||
unset($rFields['stream_info']);
|
||||
}
|
||||
StreamStateWriter::update($rStreamID, intval(SERVER_ID), $rFields, self::db());
|
||||
}
|
||||
|
||||
@@ -2152,9 +2157,11 @@ class StreamProcess {
|
||||
*
|
||||
* @param int $rStreamID Stream id.
|
||||
* @param bool $rStop Mark the stream as fully stopped (not just restarting).
|
||||
* @param bool $rIdle An on-demand stream stopped for having no viewers:
|
||||
* its stream_info is kept (see resetStreamServerRow).
|
||||
* @return mixed Stop result.
|
||||
*/
|
||||
public static function stopStream(int $rStreamID, bool $rStop = false) {
|
||||
public static function stopStream(int $rStreamID, bool $rStop = false, bool $rIdle = false) {
|
||||
// A supervised stream is released FIRST: its producer dying is exactly
|
||||
// what the fanout supervisor restarts, so killing it before the release
|
||||
// would have the daemon start a replacement and the stream refuse to stop.
|
||||
@@ -2187,7 +2194,7 @@ class StreamProcess {
|
||||
|
||||
if ($rStop) {
|
||||
shell_exec('rm -f ' . DELAY_PATH . intval($rStreamID) . '_*');
|
||||
self::resetStreamServerRow($rStreamID, true);
|
||||
self::resetStreamServerRow($rStreamID, true, $rIdle);
|
||||
self::updateStream($rStreamID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use XcVm\Domain\Stream\StreamProcess;
|
||||
use XcVm\Domain\Stream\StreamStateWriter;
|
||||
use XcVm\Infrastructure\Database\DatabaseFactory;
|
||||
|
||||
/**
|
||||
* An on-demand stream stopped for having no viewers keeps its stream_info: an
|
||||
* adaptive master lists a variant by its resolution and bitrate, so an idle
|
||||
* variant that lost them could never be offered, nor started (issue #61). Any
|
||||
* other stop still clears it.
|
||||
*/
|
||||
final class IdleOnDemandStreamInfoTest extends TestCase {
|
||||
/** @var list<array<string, mixed>> */
|
||||
private array $rWrites = [];
|
||||
|
||||
protected function setUp(): void {
|
||||
defined('SERVER_ID') || define('SERVER_ID', 1);
|
||||
DatabaseFactory::set(new TestDb());
|
||||
StreamStateWriter::useSink(function (string $rWhere, array $rFields): bool {
|
||||
$this->rWrites[] = $rFields;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
StreamStateWriter::useSink(null);
|
||||
DatabaseFactory::reset();
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> the fields the stop writes */
|
||||
private function reset(bool $rKeepInfo): array {
|
||||
(new ReflectionMethod(StreamProcess::class, 'resetStreamServerRow'))->invoke(null, 42, true, $rKeepInfo);
|
||||
$this->assertCount(1, $this->rWrites);
|
||||
return $this->rWrites[0];
|
||||
}
|
||||
|
||||
public function testAnIdleStopKeepsStreamInfo(): void {
|
||||
$rFields = $this->reset(true);
|
||||
$this->assertArrayNotHasKey('stream_info', $rFields);
|
||||
$this->assertArrayHasKey('pid', $rFields);
|
||||
$this->assertNull($rFields['pid']);
|
||||
$this->assertSame(0, $rFields['stream_status']);
|
||||
}
|
||||
|
||||
public function testAnyOtherStopClearsStreamInfo(): void {
|
||||
$rFields = $this->reset(false);
|
||||
$this->assertArrayHasKey('stream_info', $rFields);
|
||||
$this->assertNull($rFields['stream_info']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user