diff options
Diffstat (limited to 'server/lib/hls.ts')
-rw-r--r-- | server/lib/hls.ts | 82 |
1 files changed, 37 insertions, 45 deletions
diff --git a/server/lib/hls.ts b/server/lib/hls.ts index 0e77ab9fa..0828a2d0f 100644 --- a/server/lib/hls.ts +++ b/server/lib/hls.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, stat, writeFile } from 'fs-extra' | 1 | import { close, ensureDir, move, open, outputJSON, read, readFile, remove, stat, writeFile } from 'fs-extra' |
2 | import { flatten, uniq } from 'lodash' | 2 | import { flatten, uniq } from 'lodash' |
3 | import { basename, dirname, join } from 'path' | 3 | import { basename, dirname, join } from 'path' |
4 | import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models' | 4 | import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models' |
@@ -8,11 +8,12 @@ import { logger } from '../helpers/logger' | |||
8 | import { doRequest, doRequestAndSaveToFile } from '../helpers/requests' | 8 | import { doRequest, doRequestAndSaveToFile } from '../helpers/requests' |
9 | import { generateRandomString } from '../helpers/utils' | 9 | import { generateRandomString } from '../helpers/utils' |
10 | import { CONFIG } from '../initializers/config' | 10 | import { CONFIG } from '../initializers/config' |
11 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' | 11 | import { P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' |
12 | import { sequelizeTypescript } from '../initializers/database' | 12 | import { sequelizeTypescript } from '../initializers/database' |
13 | import { VideoFileModel } from '../models/video/video-file' | 13 | import { VideoFileModel } from '../models/video/video-file' |
14 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 14 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' |
15 | import { getHlsResolutionPlaylistFilename, getVideoFilePath } from './video-paths' | 15 | import { getHlsResolutionPlaylistFilename } from './paths' |
16 | import { VideoPathManager } from './video-path-manager' | ||
16 | 17 | ||
17 | async function updateStreamingPlaylistsInfohashesIfNeeded () { | 18 | async function updateStreamingPlaylistsInfohashesIfNeeded () { |
18 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() | 19 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() |
@@ -31,75 +32,66 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () { | |||
31 | } | 32 | } |
32 | 33 | ||
33 | async function updateMasterHLSPlaylist (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { | 34 | async function updateMasterHLSPlaylist (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { |
34 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | ||
35 | |||
36 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] | 35 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] |
37 | 36 | ||
38 | const masterPlaylistPath = join(directory, playlist.playlistFilename) | ||
39 | |||
40 | for (const file of playlist.VideoFiles) { | 37 | for (const file of playlist.VideoFiles) { |
41 | const playlistFilename = getHlsResolutionPlaylistFilename(file.filename) | 38 | const playlistFilename = getHlsResolutionPlaylistFilename(file.filename) |
42 | 39 | ||
43 | // If we did not generated a playlist for this resolution, skip | 40 | await VideoPathManager.Instance.makeAvailableVideoFile(playlist, file, async videoFilePath => { |
44 | const filePlaylistPath = join(directory, playlistFilename) | 41 | const size = await getVideoStreamSize(videoFilePath) |
45 | if (await pathExists(filePlaylistPath) === false) continue | ||
46 | |||
47 | const videoFilePath = getVideoFilePath(playlist, file) | ||
48 | 42 | ||
49 | const size = await getVideoStreamSize(videoFilePath) | 43 | const bandwidth = 'BANDWIDTH=' + video.getBandwidthBits(file) |
44 | const resolution = `RESOLUTION=${size.width}x${size.height}` | ||
50 | 45 | ||
51 | const bandwidth = 'BANDWIDTH=' + video.getBandwidthBits(file) | 46 | let line = `#EXT-X-STREAM-INF:${bandwidth},${resolution}` |
52 | const resolution = `RESOLUTION=${size.width}x${size.height}` | 47 | if (file.fps) line += ',FRAME-RATE=' + file.fps |
53 | 48 | ||
54 | let line = `#EXT-X-STREAM-INF:${bandwidth},${resolution}` | 49 | const codecs = await Promise.all([ |
55 | if (file.fps) line += ',FRAME-RATE=' + file.fps | 50 | getVideoStreamCodec(videoFilePath), |
51 | getAudioStreamCodec(videoFilePath) | ||
52 | ]) | ||
56 | 53 | ||
57 | const codecs = await Promise.all([ | 54 | line += `,CODECS="${codecs.filter(c => !!c).join(',')}"` |
58 | getVideoStreamCodec(videoFilePath), | ||
59 | getAudioStreamCodec(videoFilePath) | ||
60 | ]) | ||
61 | 55 | ||
62 | line += `,CODECS="${codecs.filter(c => !!c).join(',')}"` | 56 | masterPlaylists.push(line) |
63 | 57 | masterPlaylists.push(playlistFilename) | |
64 | masterPlaylists.push(line) | 58 | }) |
65 | masterPlaylists.push(playlistFilename) | ||
66 | } | 59 | } |
67 | 60 | ||
68 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') | 61 | await VideoPathManager.Instance.makeAvailablePlaylistFile(playlist, playlist.playlistFilename, masterPlaylistPath => { |
62 | return writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') | ||
63 | }) | ||
69 | } | 64 | } |
70 | 65 | ||
71 | async function updateSha256VODSegments (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { | 66 | async function updateSha256VODSegments (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { |
72 | const json: { [filename: string]: { [range: string]: string } } = {} | 67 | const json: { [filename: string]: { [range: string]: string } } = {} |
73 | 68 | ||
74 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | ||
75 | |||
76 | // For all the resolutions available for this video | 69 | // For all the resolutions available for this video |
77 | for (const file of playlist.VideoFiles) { | 70 | for (const file of playlist.VideoFiles) { |
78 | const rangeHashes: { [range: string]: string } = {} | 71 | const rangeHashes: { [range: string]: string } = {} |
79 | 72 | ||
80 | const videoPath = getVideoFilePath(playlist, file) | 73 | await VideoPathManager.Instance.makeAvailableVideoFile(playlist, file, videoPath => { |
81 | const resolutionPlaylistPath = join(playlistDirectory, getHlsResolutionPlaylistFilename(file.filename)) | ||
82 | |||
83 | // Maybe the playlist is not generated for this resolution yet | ||
84 | if (!await pathExists(resolutionPlaylistPath)) continue | ||
85 | 74 | ||
86 | const playlistContent = await readFile(resolutionPlaylistPath) | 75 | return VideoPathManager.Instance.makeAvailableResolutionPlaylistFile(playlist, file, async resolutionPlaylistPath => { |
87 | const ranges = getRangesFromPlaylist(playlistContent.toString()) | 76 | const playlistContent = await readFile(resolutionPlaylistPath) |
77 | const ranges = getRangesFromPlaylist(playlistContent.toString()) | ||
88 | 78 | ||
89 | const fd = await open(videoPath, 'r') | 79 | const fd = await open(videoPath, 'r') |
90 | for (const range of ranges) { | 80 | for (const range of ranges) { |
91 | const buf = Buffer.alloc(range.length) | 81 | const buf = Buffer.alloc(range.length) |
92 | await read(fd, buf, 0, range.length, range.offset) | 82 | await read(fd, buf, 0, range.length, range.offset) |
93 | 83 | ||
94 | rangeHashes[`${range.offset}-${range.offset + range.length - 1}`] = sha256(buf) | 84 | rangeHashes[`${range.offset}-${range.offset + range.length - 1}`] = sha256(buf) |
95 | } | 85 | } |
96 | await close(fd) | 86 | await close(fd) |
97 | 87 | ||
98 | const videoFilename = file.filename | 88 | const videoFilename = file.filename |
99 | json[videoFilename] = rangeHashes | 89 | json[videoFilename] = rangeHashes |
90 | }) | ||
91 | }) | ||
100 | } | 92 | } |
101 | 93 | ||
102 | const outputPath = join(playlistDirectory, playlist.segmentsSha256Filename) | 94 | const outputPath = VideoPathManager.Instance.getFSHLSOutputPath(video, playlist.segmentsSha256Filename) |
103 | await outputJSON(outputPath, json) | 95 | await outputJSON(outputPath, json) |
104 | } | 96 | } |
105 | 97 | ||