diff --git a/app/config/roles.php b/app/config/roles.php
index 72b1d505bb..b3891a1769 100644
--- a/app/config/roles.php
+++ b/app/config/roles.php
@@ -19,6 +19,8 @@ $member = [
'avatars.read',
'execution.read',
'execution.write',
+ 'videos.read',
+ 'videos.write',
];
$admins = [
@@ -49,6 +51,8 @@ $admins = [
'functions.write',
'execution.read',
'execution.write',
+ 'videos.read',
+ 'videos.write',
];
return [
@@ -62,6 +66,7 @@ return [
'files.read',
'locale.read',
'avatars.read',
+ 'videos.read',
],
],
Auth::USER_ROLE_MEMBER => [
diff --git a/app/controllers/api/videos.php b/app/controllers/api/videos.php
index d4d78c5ad2..9271618c35 100644
--- a/app/controllers/api/videos.php
+++ b/app/controllers/api/videos.php
@@ -717,11 +717,11 @@ App::get('/v1/videos/:videoId/streams/:streamId')
}
$baseUrl = 'http://127.0.0.1/v1/videos/' . $videoId . '/streams/' . $streamId;
+ $subtitles = Authorization::skip(fn() => $dbForProject->find('videos_subtitles', [new Query('videoId', Query::TYPE_EQUAL, [$video->getId()])]));
if ($streamId === 'hls') {
- $subtitles = Authorization::skip(fn() => $dbForProject->find('videos_subtitles', [new Query('videoId', Query::TYPE_EQUAL, [$video->getId()])], 12, 0, [], ['ASC']));
$paramsSubtitles = [];
- foreach ($subtitles as $subtitle) {
+ foreach ($subtitles ?? [] as $subtitle) {
$paramsSubtitles[] = [
'name' => $subtitle->getAttribute('name'),
'code' => $subtitle->getAttribute('code'),
@@ -731,7 +731,7 @@ App::get('/v1/videos/:videoId/streams/:streamId')
}
$paramsRenditions = [];
- foreach ($renditions as $rendition) {
+ foreach ($renditions ?? [] as $rendition) {
$paramsRenditions[] = [
'bandwidth' => ($rendition->getAttribute('videoBitrate') + $rendition->getAttribute('audioBitrate')),
'resolution' => $rendition->getAttribute('width') . 'X' . $rendition->getAttribute('height'),
@@ -746,22 +746,24 @@ App::get('/v1/videos/:videoId/streams/:streamId')
$template->setParam('paramsRenditions', $paramsRenditions);
$response->setContentType('application/x-mpegurl')->send($template->render(false));
} else {
+ $adaptationsSubs = [];
$adaptations = [];
$adaptationId = 0;
- foreach ($renditions as $rendition) {
+ foreach ($renditions ?? [] as $rendition) {
$metadata = $rendition->getAttribute('metadata');
$xml = simplexml_load_string($metadata['mpd']);
if (empty($xml)) {
continue;
}
$representationId = 0;
- foreach ($xml->Period->AdaptationSet as $adaptation) {
+ foreach ($xml->Period->AdaptationSet ?? [] as $adaptation) {
$representation = [];
$representation['id'] = $representationId;
$attributes = (array)$adaptation->Representation->attributes();
$representation['attributes'] = $attributes['@attributes'] ?? [];
$attributes = (array)$adaptation->Representation->SegmentList->attributes();
- $representation['SegmentList']['attributes'] = $attributes['@attributes'] ?? [];
+ $representation['segmentList']['attributes'] = $attributes['@attributes'] ?? [];
+
$segments = Authorization::skip(fn() => $dbForProject->find('videos_renditions_segments', [
new Query('renditionId', Query::TYPE_EQUAL, [$rendition->getId()]),
new Query('representationId', Query::TYPE_EQUAL, [$representationId]),
@@ -771,23 +773,33 @@ App::get('/v1/videos/:videoId/streams/:streamId')
}
foreach ($segments ?? [] as $segment) {
if ($segment->getAttribute('isInit')) {
- $representation['SegmentList']['Initialization'] = $segment->getId();
+ $representation['segmentList']['init'] = $segment->getId();
continue;
}
- $representation['SegmentList']['media'][] = $segment->getId();
+ $representation['segmentList']['media'][] = $segment->getId();
}
$attributes = (array)$adaptation->attributes();
$adaptations[] = ['attributes' => $attributes['@attributes'] ?? [],
'id' => $adaptationId,
'baseUrl' => $baseUrl . '/renditions/' . $rendition->getId() . '/' . 'segments/',
- 'Representation' => $representation,
+ 'representation' => $representation,
];
$adaptationId++;
$representationId++;
}
+
+ foreach ($subtitles ?? [] as $subtitle) {
+ $adaptationsSubs[] = [
+ 'id' => $adaptationId,
+ 'baseUrl' => $baseUrl . '/subtitles/' . $subtitle->getId(),
+ 'code' => $subtitle->getAttribute('code'),
+ ];
+ $adaptationId++;
+ }
}
$template = new View(__DIR__ . '/../../views/videos/dash.phtml');
+ $template->setParam('subtitles', $adaptationsSubs);
$template->setParam('params', $adaptations);
$response->setContentType('application/dash+xml')->send($template->render(false));
}
@@ -982,7 +994,7 @@ App::get('/v1/videos/:videoId/streams/:streamId/subtitles/:subtitleId')
$template->setParam('paramsSegments', $paramsSegments);
$response->setContentType('application/x-mpegurl')->send($template->render(false));
} else {
- $output = $deviceVideos->read($deviceVideos->getPath($subtitle->getAttribute('videoId') . $subtitle->getAttribute('fileId') . '.vtt'));
+ $output = $deviceVideos->read($deviceVideos->getPath($subtitle->getAttribute('videoId')) . '/' . $subtitle->getId() . '.vtt');
$response->setContentType('text/vtt')->send($output);
}
});
diff --git a/app/init.php b/app/init.php
index 1eeeee2ade..2ddef1e77d 100644
--- a/app/init.php
+++ b/app/init.php
@@ -844,8 +844,8 @@ App::setResource('project', function ($dbForConsole, $request, $console) {
/** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Document $console */
- //$projectId = 'dev';
- $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', 'console'));
+ $projectId = 'dev';
+ //$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', 'console'));
if ($projectId === 'console') {
return $console;
diff --git a/app/views/videos/dash.phtml b/app/views/videos/dash.phtml
index 40312b9774..79c1d0f257 100644
--- a/app/views/videos/dash.phtml
+++ b/app/views/videos/dash.phtml
@@ -8,7 +8,7 @@ echo ''; ?>
getParam('params', []) as $param):
- $representation = $param['Representation'];
+ $representation = $param['representation'];
?>
id="" contentType="" startWithSAP="" segmentAlignment="" bitstreamSwitching="" frameRate="" maxWidth="" par="" lang="">
id="" mimeType="" codecs="" width="" height="" sar="" audioSamplingRate="">
@@ -16,10 +16,10 @@ echo ''; ?>
- timescale="" duration="" startNumber="">
-
-
-
+ timescale="" duration="" startNumber="">
+
+
+
@@ -27,5 +27,13 @@ echo ''; ?>
+ getParam('subtitles', []) as $subtitle):?>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/workers/transcoding.php b/app/workers/transcoding.php
index 8cff6ca778..88d0989ebf 100644
--- a/app/workers/transcoding.php
+++ b/app/workers/transcoding.php
@@ -187,16 +187,17 @@ class TranscodingV1 extends Worker
}
$ext = pathinfo($subtitleFileName, PATHINFO_EXTENSION);
+ $subtitlePath = $this->inDir . $subtitle->getId() . '.vtt';
if ($ext === 'srt') {
$srt = new SubripFile($this->inDir . $subtitleFileName);
- $srt->convertTo('webvtt')->save($this->inDir . $this->args['videoId'] . '.vtt');
+ $srt->convertTo('webvtt')->save($subtitlePath);
}
$subs[] = [
'name' => $subtitle->getAttribute('name'),
'code' => $subtitle->getAttribute('code'),
- 'path' => $this->inDir . $this->args['videoId'] . '.vtt',
+ 'path' => $subtitlePath,
];
}
@@ -281,36 +282,36 @@ class TranscodingV1 extends Worker
$query->setAttribute('endedAt', time());
Authorization::skip(fn() => $this->database->updateDocument($collection, $query->getId(), $query));
- if (!empty($subtitles)) {
- foreach ($subtitles as $subtitle) {
- if ($profile['stream'] === 'hls') {
- $m3u8 = $this->parseM3u8($this->outPath . '_subtitles_' . $subtitle['code'] . '.m3u8');
- foreach ($m3u8['segments'] as $segment) {
- Authorization::skip(function () use ($segment, $project, $subtitle, $renditionRootPath) {
- return $this->database->createDocument('videos_subtitles_segments', new Document([
- 'subtitleId' => $subtitle->getId(),
- 'fileName' => $segment['fileName'],
- 'path' => $renditionRootPath ,
- 'duration' => $segment['duration'],
- ]));
- });
- }
- $subtitle->setAttribute('targetDuration', $m3u8['targetDuration']);
+ foreach ($subtitles ?? [] as $subtitle) {
+ if ($profile['stream'] === 'hls') {
+ $m3u8 = $this->parseM3u8($this->outPath . '_subtitles_' . $subtitle['code'] . '.m3u8');
+ foreach ($m3u8['segments'] ?? [] as $segment) {
+ Authorization::skip(function () use ($segment, $project, $subtitle, $renditionRootPath) {
+ return $this->database->createDocument('videos_subtitles_segments', new Document([
+ 'subtitleId' => $subtitle->getId(),
+ 'fileName' => $segment['fileName'],
+ 'path' => $renditionRootPath ,
+ 'duration' => $segment['duration'],
+ ]));
+ });
}
-
- $subtitle->setAttribute('status', self::STATUS_READY);
- $subtitle->setAttribute('path', $renditionRootPath);
- Authorization::skip(fn() => $this->database->updateDocument(
- 'videos_subtitles',
- $subtitle->getId(),
- $subtitle
- ));
+ $subtitle->setAttribute('targetDuration', $m3u8['targetDuration']);
+ } else {
+ $this->getFilesDevice($project->getId())->transfer($this->inDir . $subtitle->getId() . '.vtt', $this->outDir . $subtitle->getId() . '.vtt', $this->getFilesDevice($project->getId()));
}
+
+ $subtitle->setAttribute('status', self::STATUS_READY);
+ $subtitle->setAttribute('path', $renditionRootPath);
+ Authorization::skip(fn() => $this->database->updateDocument(
+ 'videos_subtitles',
+ $subtitle->getId(),
+ $subtitle
+ ));
}
+
/** Upload & cleanup **/
$start = 0;
$fileNames = scandir($this->outDir);
-
foreach ($fileNames as $fileName) {
if ($fileName === '.' || $fileName === '..') {
//str_contains($fileName, '.json')) {
@@ -330,7 +331,6 @@ class TranscodingV1 extends Worker
Authorization::skip(fn() => $this->database->updateDocument($collection, $query->getId(), $query));
$start = 1;
}
-
//@unlink($this->outDir . $fileName);
}
@@ -341,7 +341,7 @@ class TranscodingV1 extends Worker
var_dump($th->getMessage());
$query->setAttribute('metadata', json_encode([
'code' => $th->getCode(),
- 'message' => substr($th->getMessage(), 0, 3800),
+ 'message' => substr($th->getMessage(), 0, 3600),
]));
$query->setAttribute('status', self::STATUS_ERROR);
@@ -359,14 +359,18 @@ class TranscodingV1 extends Worker
*/
private function transcode(string $stream, Media $video, StreamFormat $format, Representation $representation, array $subtitles): string | array
{
- $video->filters()
- ->framerate(new FFMpeg\Coordinate\FrameRate(24), 2)
- ;
+// $video->filters()
+// ->framerate(new FFMpeg\Coordinate\FrameRate(24), 2)
+// ;
$additionalParams = [
'-dn',
'-sn',
'-vf', 'scale=iw:-2:force_original_aspect_ratio=increase,setsar=1:1',
+ '-r', '24',
+ '-b_strategy', '1',
+ '-bf', '3',
+ '-g', '2'
];
$segmentSize = 8;
@@ -480,8 +484,8 @@ class TranscodingV1 extends Worker
private function getVideoSourceInfo(StreamCollection $streams): array
{
return [
- 'duration' => $streams->videos()->count()> 0 ? $streams->videos()->first()->get('duration') : '0',
- 'height' => $streams->videos()->count()> 0 ? $streams->videos()->first()->get('height') : 0,
+ 'duration' => $streams->videos()->count() > 0 ? $streams->videos()->first()->get('duration') : '0',
+ 'height' => $streams->videos()->count() > 0 ? $streams->videos()->first()->get('height') : 0,
'width' => $streams->videos()->count() > 0 ? $streams->videos()->first()->get('width') : 0,
'videoCodec' => $streams->videos()->count() > 0 ? $streams->videos()->first()->get('codec_name') : '',
'videoFramerate' => $streams->videos()->count() > 0 ? $streams->videos()->first()->get('avg_frame_rate') : '',
@@ -506,17 +510,17 @@ class TranscodingV1 extends Worker
$info['width'] = $representation->getWidth();
$info['height'] = $representation->getHeight();
- foreach ($metadata['video']['streams'] ?? [] as $streams) {
- if ($streams['codec_type'] === 'video') {
- $info['duration'] = !empty($streams['duration']) ? $streams['duration'] : '0';
- $info['videoCodec'] = !empty($streams['codec_name']) ? $streams['codec_name'] : '';
- $info['videoBitrate'] = !empty($streams['bit_rate']) ? (int)$streams['bit_rate'] : $representation->getKiloBitrate();
- $info['videoFramerate'] = !empty($streams['avg_frame_rate']) ? $streams['avg_frame_rate'] : '';
- } elseif ($streams['codec_type'] === 'audio') {
- $info['audioCodec'] = !empty($streams['codec_name']) ? $streams['codec_name'] : '' ;
- $info['audioSamplerate'] = !empty($streams['sample_rate']) ? (int)$streams['sample_rate'] : 0;
- $info['audioBitrate'] = !empty($streams['bit_rate']) ? (int)$streams['bit_rate'] : $representation->getAudioKiloBitrate();
- }
+ foreach ($metadata['video']['streams'] ?? [] as $streams) {
+ if ($streams['codec_type'] === 'video') {
+ $info['duration'] = !empty($streams['duration']) ? $streams['duration'] : '0';
+ $info['videoCodec'] = !empty($streams['codec_name']) ? $streams['codec_name'] : '';
+ $info['videoBitrate'] = !empty($streams['bit_rate']) ? (int)$streams['bit_rate'] : $representation->getKiloBitrate();
+ $info['videoFramerate'] = !empty($streams['avg_frame_rate']) ? $streams['avg_frame_rate'] : '';
+ } elseif ($streams['codec_type'] === 'audio') {
+ $info['audioCodec'] = !empty($streams['codec_name']) ? $streams['codec_name'] : '' ;
+ $info['audioSamplerate'] = !empty($streams['sample_rate']) ? (int)$streams['sample_rate'] : 0;
+ $info['audioBitrate'] = !empty($streams['bit_rate']) ? (int)$streams['bit_rate'] : $representation->getAudioKiloBitrate();
+ }
}
return $info;
}