aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/video-transcoding.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/video-transcoding.ts')
-rw-r--r--server/lib/video-transcoding.ts91
1 files changed, 63 insertions, 28 deletions
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts
index 612d388ee..9243d1742 100644
--- a/server/lib/video-transcoding.ts
+++ b/server/lib/video-transcoding.ts
@@ -1,5 +1,5 @@
1import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' 1import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants'
2import { basename, join } from 'path' 2import { basename, extname as extnameUtil, join } from 'path'
3import { 3import {
4 canDoQuickTranscode, 4 canDoQuickTranscode,
5 getDurationFromVideoFile, 5 getDurationFromVideoFile,
@@ -16,18 +16,19 @@ import { updateMasterHLSPlaylist, updateSha256Segments } from './hls'
16import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 16import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
17import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' 17import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
18import { CONFIG } from '../initializers/config' 18import { CONFIG } from '../initializers/config'
19import { MVideoFile, MVideoWithFile, MVideoWithFileThumbnail } from '@server/typings/models' 19import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/typings/models'
20import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
21import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths'
20 22
21/** 23/**
22 * Optimize the original video file and replace it. The resolution is not changed. 24 * Optimize the original video file and replace it. The resolution is not changed.
23 */ 25 */
24async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { 26async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) {
25 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
26 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 27 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
27 const newExtname = '.mp4' 28 const newExtname = '.mp4'
28 29
29 const inputVideoFile = inputVideoFileArg ? inputVideoFileArg : video.getOriginalFile() 30 const inputVideoFile = inputVideoFileArg || video.getMaxQualityFile()
30 const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile)) 31 const videoInputPath = getVideoFilePath(video, inputVideoFile)
31 const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname) 32 const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname)
32 33
33 const transcodeType: TranscodeOptionsType = await canDoQuickTranscode(videoInputPath) 34 const transcodeType: TranscodeOptionsType = await canDoQuickTranscode(videoInputPath)
@@ -35,7 +36,7 @@ async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVi
35 : 'video' 36 : 'video'
36 37
37 const transcodeOptions: TranscodeOptions = { 38 const transcodeOptions: TranscodeOptions = {
38 type: transcodeType as any, // FIXME: typing issue 39 type: transcodeType,
39 inputPath: videoInputPath, 40 inputPath: videoInputPath,
40 outputPath: videoTranscodedPath, 41 outputPath: videoTranscodedPath,
41 resolution: inputVideoFile.resolution 42 resolution: inputVideoFile.resolution
@@ -50,7 +51,7 @@ async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVi
50 // Important to do this before getVideoFilename() to take in account the new file extension 51 // Important to do this before getVideoFilename() to take in account the new file extension
51 inputVideoFile.extname = newExtname 52 inputVideoFile.extname = newExtname
52 53
53 const videoOutputPath = video.getVideoFilePath(inputVideoFile) 54 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
54 55
55 await onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) 56 await onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
56 } catch (err) { 57 } catch (err) {
@@ -64,13 +65,12 @@ async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVi
64/** 65/**
65 * Transcode the original video file to a lower resolution. 66 * Transcode the original video file to a lower resolution.
66 */ 67 */
67async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { 68async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) {
68 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
69 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 69 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
70 const extname = '.mp4' 70 const extname = '.mp4'
71 71
72 // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed 72 // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
73 const videoInputPath = join(videosDirectory, video.getVideoFilename(video.getOriginalFile())) 73 const videoInputPath = getVideoFilePath(video, video.getMaxQualityFile())
74 74
75 const newVideoFile = new VideoFileModel({ 75 const newVideoFile = new VideoFileModel({
76 resolution, 76 resolution,
@@ -78,8 +78,8 @@ async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: Vi
78 size: 0, 78 size: 0,
79 videoId: video.id 79 videoId: video.id
80 }) 80 })
81 const videoOutputPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename(newVideoFile)) 81 const videoOutputPath = getVideoFilePath(video, newVideoFile)
82 const videoTranscodedPath = join(transcodeDirectory, video.getVideoFilename(newVideoFile)) 82 const videoTranscodedPath = join(transcodeDirectory, getVideoFilename(video, newVideoFile))
83 83
84 const transcodeOptions = { 84 const transcodeOptions = {
85 type: 'video' as 'video', 85 type: 'video' as 'video',
@@ -94,14 +94,13 @@ async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: Vi
94 return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) 94 return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath)
95} 95}
96 96
97async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution: VideoResolution) { 97async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) {
98 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
99 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 98 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
100 const newExtname = '.mp4' 99 const newExtname = '.mp4'
101 100
102 const inputVideoFile = video.getOriginalFile() 101 const inputVideoFile = video.getMaxQualityFile()
103 102
104 const audioInputPath = join(videosDirectory, video.getVideoFilename(video.getOriginalFile())) 103 const audioInputPath = getVideoFilePath(video, inputVideoFile)
105 const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname) 104 const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname)
106 105
107 // If the user updates the video preview during transcoding 106 // If the user updates the video preview during transcoding
@@ -130,7 +129,7 @@ async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution:
130 // Important to do this before getVideoFilename() to take in account the new file extension 129 // Important to do this before getVideoFilename() to take in account the new file extension
131 inputVideoFile.extname = newExtname 130 inputVideoFile.extname = newExtname
132 131
133 const videoOutputPath = video.getVideoFilePath(inputVideoFile) 132 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
134 // ffmpeg generated a new video file, so update the video duration 133 // ffmpeg generated a new video file, so update the video duration
135 // See https://trac.ffmpeg.org/ticket/5456 134 // See https://trac.ffmpeg.org/ticket/5456
136 video.duration = await getDurationFromVideoFile(videoTranscodedPath) 135 video.duration = await getDurationFromVideoFile(videoTranscodedPath)
@@ -139,33 +138,40 @@ async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution:
139 return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) 138 return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
140} 139}
141 140
142async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, isPortraitMode: boolean) { 141async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, copyCodecs: boolean, isPortraitMode: boolean) {
143 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 142 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
144 await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) 143 await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid))
145 144
146 const videoInputPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename(video.getFile(resolution))) 145 const videoFileInput = copyCodecs
146 ? video.getWebTorrentFile(resolution)
147 : video.getMaxQualityFile()
148
149 const videoOrStreamingPlaylist = videoFileInput.getVideoOrStreamingPlaylist()
150 const videoInputPath = getVideoFilePath(videoOrStreamingPlaylist, videoFileInput)
151
147 const outputPath = join(baseHlsDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) 152 const outputPath = join(baseHlsDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution))
153 const videoFilename = generateVideoStreamingPlaylistName(video.uuid, resolution)
148 154
149 const transcodeOptions = { 155 const transcodeOptions = {
150 type: 'hls' as 'hls', 156 type: 'hls' as 'hls',
151 inputPath: videoInputPath, 157 inputPath: videoInputPath,
152 outputPath, 158 outputPath,
153 resolution, 159 resolution,
160 copyCodecs,
154 isPortraitMode, 161 isPortraitMode,
155 162
156 hlsPlaylist: { 163 hlsPlaylist: {
157 videoFilename: VideoStreamingPlaylistModel.getHlsVideoName(video.uuid, resolution) 164 videoFilename
158 } 165 }
159 } 166 }
160 167
161 await transcode(transcodeOptions) 168 logger.debug('Will run transcode.', { transcodeOptions })
162 169
163 await updateMasterHLSPlaylist(video) 170 await transcode(transcodeOptions)
164 await updateSha256Segments(video)
165 171
166 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) 172 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid)
167 173
168 await VideoStreamingPlaylistModel.upsert({ 174 const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({
169 videoId: video.id, 175 videoId: video.id,
170 playlistUrl, 176 playlistUrl,
171 segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid), 177 segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid),
@@ -173,15 +179,44 @@ async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoReso
173 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, 179 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION,
174 180
175 type: VideoStreamingPlaylistType.HLS 181 type: VideoStreamingPlaylistType.HLS
182 }, { returning: true }) as [ MStreamingPlaylistFilesVideo, boolean ]
183 videoStreamingPlaylist.Video = video
184
185 const newVideoFile = new VideoFileModel({
186 resolution,
187 extname: extnameUtil(videoFilename),
188 size: 0,
189 fps: -1,
190 videoStreamingPlaylistId: videoStreamingPlaylist.id
176 }) 191 })
192
193 const videoFilePath = getVideoFilePath(videoStreamingPlaylist, newVideoFile)
194 const stats = await stat(videoFilePath)
195
196 newVideoFile.size = stats.size
197 newVideoFile.fps = await getVideoFileFPS(videoFilePath)
198
199 await createTorrentAndSetInfoHash(videoStreamingPlaylist, newVideoFile)
200
201 const updatedVideoFile = await newVideoFile.save()
202
203 videoStreamingPlaylist.VideoFiles = await videoStreamingPlaylist.$get('VideoFiles') as VideoFileModel[]
204 videoStreamingPlaylist.VideoFiles.push(updatedVideoFile)
205
206 video.setHLSPlaylist(videoStreamingPlaylist)
207
208 await updateMasterHLSPlaylist(video)
209 await updateSha256Segments(video)
210
211 return video
177} 212}
178 213
179// --------------------------------------------------------------------------- 214// ---------------------------------------------------------------------------
180 215
181export { 216export {
182 generateHlsPlaylist, 217 generateHlsPlaylist,
183 optimizeVideofile, 218 optimizeOriginalVideofile,
184 transcodeOriginalVideofile, 219 transcodeNewResolution,
185 mergeAudioVideofile 220 mergeAudioVideofile
186} 221}
187 222
@@ -196,7 +231,7 @@ async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoF
196 videoFile.size = stats.size 231 videoFile.size = stats.size
197 videoFile.fps = fps 232 videoFile.fps = fps
198 233
199 await video.createTorrentAndSetInfoHash(videoFile) 234 await createTorrentAndSetInfoHash(video, videoFile)
200 235
201 const updatedVideoFile = await videoFile.save() 236 const updatedVideoFile = await videoFile.save()
202 237