mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
dash subtitles
This commit is contained in:
@@ -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 => [
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -8,7 +8,7 @@ echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
|
||||
<ServiceDescription id="0"></ServiceDescription>
|
||||
<Period id="0" start="PT0.0S">
|
||||
<?php foreach ($this->getParam('params', []) as $param):
|
||||
$representation = $param['Representation'];
|
||||
$representation = $param['representation'];
|
||||
?>
|
||||
<AdaptationSet <?php if (isset($param['id'])):?> id="<?php echo $param['id']; ?>"<?php endif;?><?php if (isset($param['attributes']['contentType'])): ?> contentType="<?php echo $param['attributes']['contentType']; ?>"<?php endif;?><?php if (isset($param['attributes']['startWithSAP'])): ?> startWithSAP="<?php echo $param['attributes']['startWithSAP']; ?>"<?php endif;?><?php if (isset($param['attributes']['segmentAlignment'])): ?> segmentAlignment="<?php echo $param['attributes']['segmentAlignment']; ?>"<?php endif;?><?php if (isset($param['attributes']['bitstreamSwitching'])): ?> bitstreamSwitching="<?php echo $param['attributes']['bitstreamSwitching']; ?>"<?php endif;?><?php if (isset($param['attributes']['frameRate'])): ?> frameRate="<?php echo $param['attributes']['frameRate']; ?>"<?php endif;?><?php if (isset($param['attributes']['maxWidth'])): ?> maxWidth="<?php echo $param['attributes']['maxWidth']; ?>"<?php endif;?><?php if (isset($param['attributes']['par'])): ?> par="<?php echo $param['attributes']['par']; ?>"<?php endif;?><?php if (isset($param['attributes']['lang'])): ?> lang="<?php echo $param['attributes']['lang']; ?>"<?php endif;?>>
|
||||
<Representation <?php if (isset($representation['attributes']['id'])):?> id="<?php echo $representation['attributes']['id']; ?>"<?php endif;?><?php if (isset($representation['attributes']['mimeType'])):?> mimeType="<?php echo $representation['attributes']['mimeType']; ?>"<?php endif;?><?php if (isset($representation['attributes']['codecs'])):?> codecs="<?php echo $representation['attributes']['codecs']; ?>"<?php endif;?><?php if (isset($representation['attributes']['width'])):?> width="<?php echo $representation['attributes']['width']; ?>"<?php endif;?><?php if (isset($representation['attributes']['height'])):?> height="<?php echo $representation['attributes']['height']; ?>"<?php endif;?><?php if (isset($representation['attributes']['sar'])):?> sar="<?php echo $representation['attributes']['sar']; ?>"<?php endif;?><?php if (isset($representation['attributes']['audioSamplingRate'])):?> audioSamplingRate="<?php echo $representation['attributes']['audioSamplingRate']; ?>"<?php endif;?>>
|
||||
@@ -16,10 +16,10 @@ echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
|
||||
<?php if (isset($param['attributes']['contentType']) && $param['attributes']['contentType'] === 'audio' ):?>
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
|
||||
<?php endif;?>
|
||||
<SegmentList<?php if (isset($representation['SegmentList']['attributes']['timescale'])): ?> timescale="<?php echo $representation['SegmentList']['attributes']['timescale']; ?>"<?php endif;?><?php if (isset($representation['SegmentList']['attributes']['duration'])): ?> duration="<?php echo $representation['SegmentList']['attributes']['duration']; ?>"<?php endif;?><?php if (isset($representation['SegmentList']['attributes']['startNumber'])): ?> startNumber="<?php echo $representation['SegmentList']['attributes']['startNumber']; ?>"<?php endif;?>>
|
||||
<Initialization sourceURL="<?php if (isset($representation['SegmentList']['Initialization'])):?><?php echo $representation['SegmentList']['Initialization']; ?><?php endif;?>"/>
|
||||
<?php if (isset($representation['SegmentList']['media'])):?>
|
||||
<?php foreach ($representation['SegmentList']['media'] as $segment): ?>
|
||||
<SegmentList<?php if (isset($representation['segmentList']['attributes']['timescale'])): ?> timescale="<?php echo $representation['segmentList']['attributes']['timescale']; ?>"<?php endif;?><?php if (isset($representation['segmentList']['attributes']['duration'])): ?> duration="<?php echo $representation['segmentList']['attributes']['duration']; ?>"<?php endif;?><?php if (isset($representation['segmentList']['attributes']['startNumber'])): ?> startNumber="<?php echo $representation['segmentList']['attributes']['startNumber']; ?>"<?php endif;?>>
|
||||
<Initialization sourceURL="<?php if (isset($representation['segmentList']['init'])):?><?php echo $representation['segmentList']['init']; ?><?php endif;?>"/>
|
||||
<?php if (isset($representation['segmentList']['media'])):?>
|
||||
<?php foreach ($representation['segmentList']['media'] as $segment): ?>
|
||||
<SegmentURL media="<?php if (isset($segment)):?><?php echo $segment; ?><?php endif;?>"/>
|
||||
<?php endforeach; ?>
|
||||
<?php endif;?>
|
||||
@@ -27,5 +27,13 @@ echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($this->getParam('subtitles', []) as $subtitle):?>
|
||||
<AdaptationSet id="<?php if (isset($subtitle['id'])): echo $subtitle['id']; endif;?>" contentType="text" lang="<?php if (isset($subtitle['code'])): echo $subtitle['code']; endif;?>" mimeType="text/vtt">
|
||||
<Role value="main" />
|
||||
<Representation id="<?php if (isset($subtitle['id'])): echo $subtitle['id']; endif;?>" bandwidth="1000">
|
||||
<BaseURL><?php if (isset($subtitle['baseUrl'])): echo $subtitle['baseUrl']; endif;?></BaseURL>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<?php endforeach; ?>
|
||||
</Period>
|
||||
</MPD>
|
||||
+48
-44
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user