aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/hls.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/hls.ts')
-rw-r--r--server/lib/hls.ts82
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 @@
1import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, stat, writeFile } from 'fs-extra' 1import { close, ensureDir, move, open, outputJSON, read, readFile, remove, stat, writeFile } from 'fs-extra'
2import { flatten, uniq } from 'lodash' 2import { flatten, uniq } from 'lodash'
3import { basename, dirname, join } from 'path' 3import { basename, dirname, join } from 'path'
4import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models' 4import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models'
@@ -8,11 +8,12 @@ import { logger } from '../helpers/logger'
8import { doRequest, doRequestAndSaveToFile } from '../helpers/requests' 8import { doRequest, doRequestAndSaveToFile } from '../helpers/requests'
9import { generateRandomString } from '../helpers/utils' 9import { generateRandomString } from '../helpers/utils'
10import { CONFIG } from '../initializers/config' 10import { CONFIG } from '../initializers/config'
11import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' 11import { P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants'
12import { sequelizeTypescript } from '../initializers/database' 12import { sequelizeTypescript } from '../initializers/database'
13import { VideoFileModel } from '../models/video/video-file' 13import { VideoFileModel } from '../models/video/video-file'
14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
15import { getHlsResolutionPlaylistFilename, getVideoFilePath } from './video-paths' 15import { getHlsResolutionPlaylistFilename } from './paths'
16import { VideoPathManager } from './video-path-manager'
16 17
17async function updateStreamingPlaylistsInfohashesIfNeeded () { 18async 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
33async function updateMasterHLSPlaylist (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { 34async 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
71async function updateSha256VODSegments (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { 66async 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