dash subtitles

This commit is contained in:
shimon
2022-08-04 14:58:25 +03:00
parent 2f1a116e22
commit 6ca361d0b0
5 changed files with 90 additions and 61 deletions
+5
View File
@@ -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 => [
+22 -10
View File
@@ -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
View File
@@ -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;
+13 -5
View File
@@ -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
View File
@@ -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;
}