Browse Source

Added rtmp stats for users, fixes #5

master
Shyim 6 years ago
parent
commit
8d78961074
  1. 6
      config/services.yaml
  2. 2
      public/static/js/app.js
  3. 25
      public/theme/src/components/ReCast/Streams/SetupStream.vue
  4. 9
      src/Command/CronRunnerCommand.php
  5. 8
      src/Command/GenerateConfigCommand.php
  6. 34
      src/Component/Nginx/ConfigGenerator.php
  7. 45
      src/Component/Nginx/Stats.php
  8. 37
      src/Controller/Streams.php

6
config/services.yaml

@ -36,11 +36,11 @@ services: @@ -36,11 +36,11 @@ services:
class: App\Component\ServiceManager
arguments: [!tagged recast.service]
App\Component\NginxConfigGenerator:
class: App\Component\NginxConfigGenerator
App\Component\Nginx\ConfigGenerator:
class: App\Component\Nginx\ConfigGenerator
autowire: true
bind:
$nginxFolder: '%nginxConfigFolder%'
$nginxConfigFolder: '%nginxConfigFolder%'
$appHost: '%appHost%'
# add more service definitions when explicit configuration is needed

2
public/static/js/app.js

File diff suppressed because one or more lines are too long

25
public/theme/src/components/ReCast/Streams/SetupStream.vue

@ -8,6 +8,19 @@ @@ -8,6 +8,19 @@
<fg-input label="URL" v-model="stream.streamUrl" disabled="true"></fg-input>
<fg-input label="Stream Key" v-model="stream.streamKey" disabled="true"></fg-input>
<div v-if="stats.active">
<h3>Stream Statistics</h3>
<p>
<strong>Bytes received</strong> {{ stats.bytes_in / 1048576 | toNumber }} MiB<br>
<strong>Bytes sent</strong> {{ stats.bytes_out / 1048576 | toNumber }} MiB<br>
<strong>Current bandwidth in</strong> {{ stats.bw_in / 1000000 | toNumber }} Mb/s<br>
<strong>Current bandwidth out</strong> {{ stats.bw_out / 1000000 | toNumber }} Mb/s<br>
<strong>Res: </strong> {{ stats.meta.video.width }}x{{ stats.meta.video.height }}<br>
<strong>FPS: </strong> {{ stats.meta.video.frame_rate }}
</p>
</div>
<h4>OBS</h4>
<ul>
@ -26,13 +39,23 @@ @@ -26,13 +39,23 @@
export default {
data() {
return {
stream: {}
stream: {},
stats: {}
}
},
mounted() {
this.axios.get('/streams/one?id=' + this.$route.params.id).then(response => {
this.stream = response.data;
});
this.axios.get(`/streams/${this.$route.params.id}/stats`).then(response => {
this.stats = response.data;
})
},
filters: {
toNumber: function (value) {
return value.toFixed(2);
}
}
}

9
src/Command/CronRunnerCommand.php

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
namespace App\Command;
use App\Component\NginxConfigGenerator;
use App\Component\Nginx\ConfigGenerator;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@ -23,8 +23,9 @@ class CronRunnerCommand extends Command implements ContainerAwareInterface @@ -23,8 +23,9 @@ class CronRunnerCommand extends Command implements ContainerAwareInterface
* @var Connection
*/
private $connection;
/**
* @var NginxConfigGenerator
* @var ConfigGenerator
*/
private $configGenerator;
@ -32,10 +33,10 @@ class CronRunnerCommand extends Command implements ContainerAwareInterface @@ -32,10 +33,10 @@ class CronRunnerCommand extends Command implements ContainerAwareInterface
* CronRunnerCommand constructor.
* @param null|string $name
* @param Connection $connection
* @param NginxConfigGenerator $configGenerator
* @param ConfigGenerator $configGenerator
* @author Soner Sayakci <shyim@posteo.de>
*/
public function __construct(?string $name = null, Connection $connection, NginxConfigGenerator $configGenerator)
public function __construct(?string $name = null, Connection $connection, ConfigGenerator $configGenerator)
{
parent::__construct($name);
$this->connection = $connection;

8
src/Command/GenerateConfigCommand.php

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
namespace App\Command;
use App\Component\NginxConfigGenerator;
use App\Component\Nginx\ConfigGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -17,17 +17,17 @@ use Symfony\Component\Console\Style\SymfonyStyle; @@ -17,17 +17,17 @@ use Symfony\Component\Console\Style\SymfonyStyle;
class GenerateConfigCommand extends Command
{
/**
* @var NginxConfigGenerator
* @var ConfigGenerator
*/
private $configGenerator;
/**
* GenerateConfigCommand constructor.
* @param null|string $name
* @param NginxConfigGenerator $configGenerator
* @param ConfigGenerator $configGenerator
* @author Soner Sayakci <shyim@posteo.de>
*/
public function __construct(?string $name = null, NginxConfigGenerator $configGenerator)
public function __construct(?string $name = null, ConfigGenerator $configGenerator)
{
parent::__construct($name);
$this->configGenerator = $configGenerator;

34
src/Component/NginxConfigGenerator.php → src/Component/Nginx/ConfigGenerator.php

@ -1,16 +1,17 @@ @@ -1,16 +1,17 @@
<?php
namespace App\Component;
namespace App\Component\Nginx;
use App\Component\ServiceManager;
use App\Entity\Endpoint;
use App\Repository\StreamsRepository;
/**
* Class NginxConfigGenerator
* Class ConfigGenerator
* @author Soner Sayakci <shyim@posteo.de>
*/
class NginxConfigGenerator
class ConfigGenerator
{
private const VHOST = "\t\tapplication %s {
\t\t\tlive on;
@ -31,6 +32,15 @@ rtmp { @@ -31,6 +32,15 @@ rtmp {
%s
\t}
}
http {
\tserver {
\t\tlisten 127.0.0.1:26765;
\t\tlocation /stat {
\t\t\trtmp_stat all;
\t\t}
\t}
}
";
/**
@ -45,7 +55,7 @@ rtmp { @@ -45,7 +55,7 @@ rtmp {
/**
* @var string
*/
private $nginxFolder;
private $nginxConfigFolder;
/**
* @var string
@ -53,18 +63,18 @@ rtmp { @@ -53,18 +63,18 @@ rtmp {
private $appHost;
/**
* NginxConfigGenerator constructor.
* ConfigGenerator constructor.
* @param StreamsRepository $repository
* @param ServiceManager $manager
* @param string $nginxFolder
* @param string $nginxConfigFolder
* @param string $appHost
* @author Soner Sayakci <shyim@posteo.de>
*/
public function __construct(StreamsRepository $repository, ServiceManager $manager, string $nginxFolder, string $appHost)
public function __construct(StreamsRepository $repository, ServiceManager $manager, string $nginxConfigFolder, string $appHost)
{
$this->repository = $repository;
$this->manager = $manager;
$this->nginxFolder = $nginxFolder;
$this->nginxConfigFolder = $nginxConfigFolder;
$this->appHost = $appHost;
}
@ -74,9 +84,9 @@ rtmp { @@ -74,9 +84,9 @@ rtmp {
*/
public function generate(): void
{
if (!file_exists($this->nginxFolder)) {
if (!mkdir($this->nginxFolder, 7777, true) && !is_dir($this->nginxFolder)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->nginxFolder));
if (!file_exists($this->nginxConfigFolder)) {
if (!mkdir($this->nginxConfigFolder, 7777, true) && !is_dir($this->nginxConfigFolder)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->nginxConfigFolder));
}
}
@ -100,7 +110,7 @@ rtmp { @@ -100,7 +110,7 @@ rtmp {
}
}
file_put_contents($this->nginxFolder . '/nginx.conf', sprintf(self::NGINX_CONF, $vhost));
file_put_contents($this->nginxConfigFolder . '/nginx.conf', sprintf(self::NGINX_CONF, $vhost));
}
/**

45
src/Component/Nginx/Stats.php

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
<?php
namespace App\Component\Nginx;
use App\Entity\Streams;
/**
* Class Stats
* @author Soner Sayakci <shyim@posteo.de>
*/
class Stats
{
/**
* @var array
*/
private $data;
/**
* Stats constructor.
* @author Soner Sayakci <shyim@posteo.de>
*/
public function __construct()
{
$stats = file_get_contents('http://127.0.0.1:26765/stat');
$xml = simplexml_load_string($stats, 'SimpleXMLElement', LIBXML_NOCDATA);
$this->data = json_decode(json_encode($xml), true);
}
/**
* @param Streams $stream
* @author Soner Sayakci <shyim@posteo.de>
* @return array
*/
public function getStatsForStream(Streams $stream): array
{
$appKey = $stream->getUser()->getUsername() . '/' . $stream->getName();
foreach ($this->data['server']['application'] as $application) {
if ($application['name'] === $appKey && isset($application['live']['stream'])) {
return $application['live']['stream'];
}
}
return [];
}
}

37
src/Controller/Streams.php

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
namespace App\Controller;
use App\Component\Nginx\Stats;
use App\Component\ServiceManager;
use App\Entity\Endpoint;
use App\Entity\User;
@ -48,7 +49,7 @@ class Streams extends Controller @@ -48,7 +49,7 @@ class Streams extends Controller
* @Route(path="/list")
* @author Soner Sayakci <shyim@posteo.de>
*/
public function streams() : Response
public function streams(): Response
{
/** @var User $user */
$user = $this->getUser();
@ -62,7 +63,7 @@ class Streams extends Controller @@ -62,7 +63,7 @@ class Streams extends Controller
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
* @author Soner Sayakci <shyim@posteo.de>
*/
public function one(Request $request) : Response
public function one(Request $request): Response
{
$stream = $this->repository->find($request->query->get('id'));
@ -85,7 +86,7 @@ class Streams extends Controller @@ -85,7 +86,7 @@ class Streams extends Controller
* @throws \Doctrine\ORM\OptimisticLockException
* @author Soner Sayakci <shyim@posteo.de>
*/
public function update(Request $request) : Response
public function update(Request $request): Response
{
$requestBody = $request->request->all();
@ -105,7 +106,7 @@ class Streams extends Controller @@ -105,7 +106,7 @@ class Streams extends Controller
$manager = $this->get('doctrine.orm.entity_manager');
try {
try {
$manager->persist($stream);
$manager->flush();
} catch (UniqueConstraintViolationException $e) {
@ -124,7 +125,7 @@ class Streams extends Controller @@ -124,7 +125,7 @@ class Streams extends Controller
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*/
public function regenerate(Request $request) : Response
public function regenerate(Request $request): Response
{
$manager = $this->get('doctrine.orm.entity_manager');
$streams = $manager->find(\App\Entity\Streams::class, $request->request->get('id'));
@ -147,7 +148,7 @@ class Streams extends Controller @@ -147,7 +148,7 @@ class Streams extends Controller
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*/
public function delete(Request $request) : Response
public function delete(Request $request): Response
{
$manager = $this->get('doctrine.orm.entity_manager');
$streams = $manager->find(\App\Entity\Streams::class, $request->request->get('id'));
@ -171,6 +172,24 @@ class Streams extends Controller @@ -171,6 +172,24 @@ class Streams extends Controller
return new JsonResponse($manager->getTemplateData());
}
/**
* @Route(path="/{id}/stats")
* @param int $id
* @param Stats $nginxStats
* @return JsonResponse
* @author Soner Sayakci <shyim@posteo.de>
*/
public function stats(Stats $nginxStats, int $id): JsonResponse
{
$stream = $this->repository->find($id);
if ($stream === null || $stream->getUserId() !== $this->getUser()->getId()) {
return new JsonResponse([]);
}
return new JsonResponse($nginxStats->getStatsForStream($stream));
}
/**
* @Route(path="/{id}/endpoints/")
* @author Soner Sayakci <shyim@posteo.de>
@ -188,7 +207,7 @@ class Streams extends Controller @@ -188,7 +207,7 @@ class Streams extends Controller
* @param int $id
* @return JsonResponse
*/
public function endpoint(int $id)
public function endpoint(int $id): JsonResponse
{
return new JsonResponse($this->endpointRepository->find($id));
}
@ -249,7 +268,7 @@ class Streams extends Controller @@ -249,7 +268,7 @@ class Streams extends Controller
* @throws \Doctrine\ORM\OptimisticLockException
* @author Soner Sayakci <shyim@posteo.de>
*/
public function toggleEndpoint(Request $request) : JsonResponse
public function toggleEndpoint(Request $request): JsonResponse
{
$id = $request->request->get('id');
$endpoint = $this->endpointRepository->find($id);
@ -277,7 +296,7 @@ class Streams extends Controller @@ -277,7 +296,7 @@ class Streams extends Controller
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*/
public function deleteEndpoint(Request $request) : Response
public function deleteEndpoint(Request $request): Response
{
$manager = $this->get('doctrine.orm.entity_manager');
$endpoint = $manager->find(Endpoint::class, $request->request->get('id'));

Loading…
Cancel
Save