1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-04 10:19:35 +02:00

Separate HLS audio and video streams

Allows:
  * The HLS player to propose an "Audio only" resolution
  * The live to output an "Audio only" resolution
  * The live to ingest and output an "Audio only" stream

 This feature is under a config for VOD videos and is enabled by default for lives

 In the future we can imagine:
  * To propose multiple audio streams for a specific video
  * To ingest an audio only VOD and just output an audio only "video"
    (the player would play the audio file and PeerTube would not
    generate additional resolutions)

This commit introduce a new way to download videos:
 * Add "/download/videos/generate/:videoId" endpoint where PeerTube can
   mux an audio only and a video only file to a mp4 container
 * The download client modal introduces a new default panel where the
   user can choose resolutions it wants to download
This commit is contained in:
Chocobozzz 2024-07-23 16:38:51 +02:00 committed by Chocobozzz
parent e77ba2dfbc
commit 816f346a60
186 changed files with 5748 additions and 2807 deletions

View file

@ -1,17 +1,17 @@
import { MutexInterface } from 'async-mutex'
import { Job } from 'bullmq'
import { ensureDir, move } from 'fs-extra/esm'
import { join } from 'path'
import { pick } from '@peertube/peertube-core-utils'
import { getVideoStreamDuration, HLSFromTSTranscodeOptions, HLSTranscodeOptions } from '@peertube/peertube-ffmpeg'
import { retryTransactionWrapper } from '@server/helpers/database-utils.js'
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent.js'
import { sequelizeTypescript } from '@server/initializers/database.js'
import { MVideo } from '@server/types/models/index.js'
import { getVideoStreamDuration } from '@peertube/peertube-ffmpeg'
import { MutexInterface } from 'async-mutex'
import { Job } from 'bullmq'
import { ensureDir, move } from 'fs-extra/esm'
import { join } from 'path'
import { CONFIG } from '../../initializers/config.js'
import { VideoFileModel } from '../../models/video/video-file.js'
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist.js'
import { renameVideoFileInPlaylist, updatePlaylistAfterFileChange } from '../hls.js'
import { renameVideoFileInPlaylist, updateM3U8AndShaPlaylist } from '../hls.js'
import { generateHLSVideoFilename, getHlsResolutionPlaylistFilename } from '../paths.js'
import { buildNewFile } from '../video-file.js'
import { VideoPathManager } from '../video-path-manager.js'
@ -28,7 +28,8 @@ export async function generateHlsPlaylistResolutionFromTS (options: {
}) {
return generateHlsPlaylistCommon({
type: 'hls-from-ts' as 'hls-from-ts',
inputPath: options.concatenatedTsFilePath,
videoInputPath: options.concatenatedTsFilePath,
...pick(options, [ 'video', 'resolution', 'fps', 'inputFileMutexReleaser', 'isAAC' ])
})
@ -37,18 +38,31 @@ export async function generateHlsPlaylistResolutionFromTS (options: {
// Generate an HLS playlist from an input file, and update the master playlist
export function generateHlsPlaylistResolution (options: {
video: MVideo
videoInputPath: string
separatedAudioInputPath: string
resolution: number
fps: number
copyCodecs: boolean
inputFileMutexReleaser: MutexInterface.Releaser
separatedAudio: boolean
job?: Job
}) {
return generateHlsPlaylistCommon({
type: 'hls' as 'hls',
inputPath: options.videoInputPath,
...pick(options, [ 'video', 'resolution', 'fps', 'copyCodecs', 'inputFileMutexReleaser', 'job' ])
...pick(options, [
'videoInputPath',
'separatedAudioInputPath',
'video',
'resolution',
'fps',
'copyCodecs',
'separatedAudio',
'inputFileMutexReleaser',
'job'
])
})
}
@ -113,7 +127,7 @@ export async function onHLSVideoFileTranscoding (options: {
const savedVideoFile = await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined)
await updatePlaylistAfterFileChange(video, playlist)
await updateM3U8AndShaPlaylist(video, playlist)
return { resolutionPlaylistPath, videoFile: savedVideoFile }
} finally {
@ -121,24 +135,43 @@ export async function onHLSVideoFileTranscoding (options: {
}
}
// ---------------------------------------------------------------------------
// Private
// ---------------------------------------------------------------------------
async function generateHlsPlaylistCommon (options: {
type: 'hls' | 'hls-from-ts'
video: MVideo
inputPath: string
videoInputPath: string
separatedAudioInputPath?: string
resolution: number
fps: number
inputFileMutexReleaser: MutexInterface.Releaser
separatedAudio?: boolean
copyCodecs?: boolean
isAAC?: boolean
job?: Job
}) {
const { type, video, inputPath, resolution, fps, copyCodecs, isAAC, job, inputFileMutexReleaser } = options
const {
type,
video,
videoInputPath,
separatedAudioInputPath,
resolution,
fps,
copyCodecs,
separatedAudio,
isAAC,
job,
inputFileMutexReleaser
} = options
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const videoTranscodedBasePath = join(transcodeDirectory, type)
@ -150,15 +183,18 @@ async function generateHlsPlaylistCommon (options: {
const resolutionPlaylistFilename = getHlsResolutionPlaylistFilename(videoFilename)
const m3u8OutputPath = join(videoTranscodedBasePath, resolutionPlaylistFilename)
const transcodeOptions = {
const transcodeOptions: HLSTranscodeOptions | HLSFromTSTranscodeOptions = {
type,
inputPath,
videoInputPath,
separatedAudioInputPath,
outputPath: m3u8OutputPath,
resolution,
fps,
copyCodecs,
separatedAudio,
isAAC,