X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Flib%2Ftranscoding%2Ftranscoding.ts;h=44e26754d52954e06fab0236ea3f98b8c3fe1a36;hb=367a9dc69975a0db01962dbb7106635fb8eb1696;hp=d55364e25c4baace27e9d163f0216eaa54a9a275;hpb=c729caf6cc34630877a0e5a1bda1719384cd0c8a;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/lib/transcoding/transcoding.ts b/server/lib/transcoding/transcoding.ts index d55364e25..44e26754d 100644 --- a/server/lib/transcoding/transcoding.ts +++ b/server/lib/transcoding/transcoding.ts @@ -1,32 +1,27 @@ -import { Job } from 'bull' +import { Job } from 'bullmq' import { copyFile, ensureDir, move, remove, stat } from 'fs-extra' import { basename, extname as extnameUtil, join } from 'path' import { toEven } from '@server/helpers/core-utils' +import { retryTransactionWrapper } from '@server/helpers/database-utils' import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' -import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoFullLight } from '@server/types/models' +import { sequelizeTypescript } from '@server/initializers/database' +import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' import { VideoResolution, VideoStorage } from '../../../shared/models/videos' -import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' import { + buildFileMetadata, canDoQuickTranscode, + computeResolutionsToTranscode, getVideoStreamDuration, - buildFileMetadata, getVideoStreamFPS, transcodeVOD, TranscodeVODOptions, TranscodeVODOptionsType } from '../../helpers/ffmpeg' import { CONFIG } from '../../initializers/config' -import { P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants' import { VideoFileModel } from '../../models/video/video-file' import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' -import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' -import { - generateHLSMasterPlaylistFilename, - generateHlsSha256SegmentsFilename, - generateHLSVideoFilename, - generateWebTorrentVideoFilename, - getHlsResolutionPlaylistFilename -} from '../paths' +import { updatePlaylistAfterFileChange } from '../hls' +import { generateHLSVideoFilename, generateWebTorrentVideoFilename, getHlsResolutionPlaylistFilename } from '../paths' import { VideoPathManager } from '../video-path-manager' import { VideoTranscodingProfilesManager } from './default-transcoding-profiles' @@ -38,7 +33,13 @@ import { VideoTranscodingProfilesManager } from './default-transcoding-profiles' */ // Optimize the original video file and replace it. The resolution is not changed. -function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile: MVideoFile, job?: Job) { +function optimizeOriginalVideofile (options: { + video: MVideoFullLight + inputVideoFile: MVideoFile + job: Job +}) { + const { video, inputVideoFile, job } = options + const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' @@ -49,7 +50,7 @@ function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile: MVid ? 'quick-transcode' : 'video' - const resolution = toEven(inputVideoFile.resolution) + const resolution = buildOriginalFileResolution(inputVideoFile.resolution) const transcodeOptions: TranscodeVODOptions = { type: transcodeType, @@ -69,6 +70,7 @@ function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile: MVid await transcodeVOD(transcodeOptions) // Important to do this before getVideoFilename() to take in account the new filename + inputVideoFile.resolution = resolution inputVideoFile.extname = newExtname inputVideoFile.filename = generateWebTorrentVideoFilename(resolution, newExtname) inputVideoFile.storage = VideoStorage.FILE_SYSTEM @@ -82,17 +84,22 @@ function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile: MVid }) } -// Transcode the original video file to a lower resolution -// We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed -function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolution: VideoResolution, isPortrait: boolean, job: Job) { +// Transcode the original video file to a lower resolution compatible with WebTorrent +function transcodeNewWebTorrentResolution (options: { + video: MVideoFullLight + resolution: VideoResolution + job: Job +}) { + const { video, resolution, job } = options + const transcodeDirectory = CONFIG.STORAGE.TMP_DIR - const extname = '.mp4' + const newExtname = '.mp4' return VideoPathManager.Instance.makeAvailableVideoFile(video.getMaxQualityFile().withVideoOrPlaylist(video), async videoInputPath => { const newVideoFile = new VideoFileModel({ resolution, - extname, - filename: generateWebTorrentVideoFilename(resolution, extname), + extname: newExtname, + filename: generateWebTorrentVideoFilename(resolution, newExtname), size: 0, videoId: video.id }) @@ -123,7 +130,6 @@ function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolution: V profile: CONFIG.TRANSCODING.PROFILE, resolution, - isPortraitMode: isPortrait, job } @@ -135,7 +141,13 @@ function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolution: V } // Merge an image with an audio file to create a video -function mergeAudioVideofile (video: MVideoFullLight, resolution: VideoResolution, job: Job) { +function mergeAudioVideofile (options: { + video: MVideoFullLight + resolution: VideoResolution + job: Job +}) { + const { video, resolution, job } = options + const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' @@ -191,16 +203,14 @@ function mergeAudioVideofile (video: MVideoFullLight, resolution: VideoResolutio // Concat TS segments from a live video to a fragmented mp4 HLS playlist async function generateHlsPlaylistResolutionFromTS (options: { - video: MVideoFullLight + video: MVideo concatenatedTsFilePath: string resolution: VideoResolution - isPortraitMode: boolean isAAC: boolean }) { return generateHlsPlaylistCommon({ video: options.video, resolution: options.resolution, - isPortraitMode: options.isPortraitMode, inputPath: options.concatenatedTsFilePath, type: 'hls-from-ts' as 'hls-from-ts', isAAC: options.isAAC @@ -209,18 +219,16 @@ async function generateHlsPlaylistResolutionFromTS (options: { // Generate an HLS playlist from an input file, and update the master playlist function generateHlsPlaylistResolution (options: { - video: MVideoFullLight + video: MVideo videoInputPath: string resolution: VideoResolution copyCodecs: boolean - isPortraitMode: boolean job?: Job }) { return generateHlsPlaylistCommon({ video: options.video, resolution: options.resolution, copyCodecs: options.copyCodecs, - isPortraitMode: options.isPortraitMode, inputPath: options.videoInputPath, type: 'hls' as 'hls', job: options.job @@ -257,6 +265,9 @@ async function onWebTorrentVideoFileTranscoding ( await createTorrentAndSetInfoHash(video, videoFile) + const oldFile = await VideoFileModel.loadWebTorrentFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution }) + if (oldFile) await video.removeWebTorrentFile(oldFile) + await VideoFileModel.customUpsert(videoFile, 'video', undefined) video.VideoFiles = await video.$get('VideoFiles') @@ -265,16 +276,15 @@ async function onWebTorrentVideoFileTranscoding ( async function generateHlsPlaylistCommon (options: { type: 'hls' | 'hls-from-ts' - video: MVideoFullLight + video: MVideo inputPath: string resolution: VideoResolution copyCodecs?: boolean isAAC?: boolean - isPortraitMode: boolean job?: Job }) { - const { type, video, inputPath, resolution, copyCodecs, isPortraitMode, isAAC, job } = options + const { type, video, inputPath, resolution, copyCodecs, isAAC, job } = options const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const videoTranscodedBasePath = join(transcodeDirectory, type) @@ -295,7 +305,6 @@ async function generateHlsPlaylistCommon (options: { resolution, copyCodecs, - isPortraitMode, isAAC, @@ -309,28 +318,15 @@ async function generateHlsPlaylistCommon (options: { await transcodeVOD(transcodeOptions) // Create or update the playlist - const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video) - - if (!playlist.playlistFilename) { - playlist.playlistFilename = generateHLSMasterPlaylistFilename(video.isLive) - } - - if (!playlist.segmentsSha256Filename) { - playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(video.isLive) - } - - playlist.p2pMediaLoaderInfohashes = [] - playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION - - playlist.type = VideoStreamingPlaylistType.HLS - - await playlist.save() + const playlist = await retryTransactionWrapper(() => { + return sequelizeTypescript.transaction(async transaction => { + return VideoStreamingPlaylistModel.loadOrGenerate(video, transaction) + }) + }) - // Build the new playlist file - const extname = extnameUtil(videoFilename) const newVideoFile = new VideoFileModel({ resolution, - extname, + extname: extnameUtil(videoFilename), size: 0, filename: videoFilename, fps: -1, @@ -338,8 +334,6 @@ async function generateHlsPlaylistCommon (options: { }) const videoFilePath = VideoPathManager.Instance.getFSVideoFileOutputPath(playlist, newVideoFile) - - // Move files from tmp transcoded directory to the appropriate place await ensureDir(VideoPathManager.Instance.getFSHLSOutputPath(video)) // Move playlist file @@ -348,6 +342,12 @@ async function generateHlsPlaylistCommon (options: { // Move video file await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true }) + // Update video duration if it was not set (in case of a live for example) + if (!video.duration) { + video.duration = await getVideoStreamDuration(videoFilePath) + await video.save() + } + const stats = await stat(videoFilePath) newVideoFile.size = stats.size @@ -356,18 +356,24 @@ async function generateHlsPlaylistCommon (options: { await createTorrentAndSetInfoHash(playlist, newVideoFile) + const oldFile = await VideoFileModel.loadHLSFile({ playlistId: playlist.id, fps: newVideoFile.fps, resolution: newVideoFile.resolution }) + if (oldFile) { + await video.removeStreamingPlaylistVideoFile(playlist, oldFile) + await oldFile.destroy() + } + const savedVideoFile = await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined) - const playlistWithFiles = playlist as MStreamingPlaylistFilesVideo - playlistWithFiles.VideoFiles = await playlist.$get('VideoFiles') - playlist.assignP2PMediaLoaderInfoHashes(video, playlistWithFiles.VideoFiles) + await updatePlaylistAfterFileChange(video, playlist) - await playlist.save() + return { resolutionPlaylistPath, videoFile: savedVideoFile } +} - video.setHLSPlaylist(playlist) +function buildOriginalFileResolution (inputResolution: number) { + if (CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION === true) return toEven(inputResolution) - await updateMasterHLSPlaylist(video, playlistWithFiles) - await updateSha256VODSegments(video, playlistWithFiles) + const resolutions = computeResolutionsToTranscode({ input: inputResolution, type: 'vod', includeInput: false, strictLower: false }) + if (resolutions.length === 0) return toEven(inputResolution) - return { resolutionPlaylistPath, videoFile: savedVideoFile } + return Math.max(...resolutions) }