mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-06 03:50:26 +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:
parent
e77ba2dfbc
commit
816f346a60
186 changed files with 5748 additions and 2807 deletions
|
@ -135,11 +135,11 @@ async function checkFFmpeg (CONFIG: { TRANSCODING: { ENABLED: boolean } }) {
|
|||
|
||||
for (const codec of canEncode) {
|
||||
if (codecs[codec] === undefined) {
|
||||
throw new Error('Unknown codec ' + codec + ' in FFmpeg.')
|
||||
throw new Error(`Codec ${codec} not found in FFmpeg.`)
|
||||
}
|
||||
|
||||
if (codecs[codec].canEncode !== true) {
|
||||
throw new Error('Unavailable encode codec ' + codec + ' in FFmpeg')
|
||||
throw new Error(`Unavailable encode codec ${codec} in FFmpeg`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,6 +222,10 @@ const CONFIG = {
|
|||
CLIENT: {
|
||||
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.client.window')),
|
||||
MAX: config.get<number>('rates_limit.client.max')
|
||||
},
|
||||
DOWNLOAD_GENERATE_VIDEO: {
|
||||
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.download_generate_video.window')),
|
||||
MAX: config.get<number>('rates_limit.download_generate_video.max')
|
||||
}
|
||||
},
|
||||
TRUST_PROXY: config.get<string[]>('trust_proxy'),
|
||||
|
@ -445,7 +449,8 @@ const CONFIG = {
|
|||
get '2160p' () { return config.get<boolean>('transcoding.resolutions.2160p') }
|
||||
},
|
||||
HLS: {
|
||||
get ENABLED () { return config.get<boolean>('transcoding.hls.enabled') }
|
||||
get ENABLED () { return config.get<boolean>('transcoding.hls.enabled') },
|
||||
get SPLIT_AUDIO_AND_VIDEO () { return config.get<boolean>('transcoding.hls.split_audio_and_video') }
|
||||
},
|
||||
WEB_VIDEOS: {
|
||||
get ENABLED () { return config.get<boolean>('transcoding.web_videos.enabled') }
|
||||
|
@ -491,6 +496,7 @@ const CONFIG = {
|
|||
get ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION () { return config.get<boolean>('live.transcoding.always_transcode_original_resolution') },
|
||||
|
||||
RESOLUTIONS: {
|
||||
get '0p' () { return config.get<boolean>('live.transcoding.resolutions.0p') },
|
||||
get '144p' () { return config.get<boolean>('live.transcoding.resolutions.144p') },
|
||||
get '240p' () { return config.get<boolean>('live.transcoding.resolutions.240p') },
|
||||
get '360p' () { return config.get<boolean>('live.transcoding.resolutions.360p') },
|
||||
|
|
|
@ -47,7 +47,7 @@ import { cpus } from 'os'
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const LAST_MIGRATION_VERSION = 860
|
||||
const LAST_MIGRATION_VERSION = 865
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -214,7 +214,7 @@ const JOB_ATTEMPTS: { [id in JobType]: number } = {
|
|||
'federate-video': 1,
|
||||
'create-user-export': 1,
|
||||
'import-user-archive': 1,
|
||||
'video-transcription': 1
|
||||
'video-transcription': 2
|
||||
}
|
||||
// Excluded keys are jobs that can be configured by admins
|
||||
const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-import'>]: number } = {
|
||||
|
@ -327,6 +327,7 @@ const AP_CLEANER = {
|
|||
const REQUEST_TIMEOUTS = {
|
||||
DEFAULT: 7000, // 7 seconds
|
||||
FILE: 30000, // 30 seconds
|
||||
VIDEO_FILE: 60000, // 1 minute
|
||||
REDUNDANCY: JOB_TTL['video-redundancy']
|
||||
}
|
||||
|
||||
|
@ -873,9 +874,10 @@ const STATIC_PATHS = {
|
|||
PRIVATE_HLS: '/static/streaming-playlists/hls/private/'
|
||||
}
|
||||
}
|
||||
const STATIC_DOWNLOAD_PATHS = {
|
||||
const DOWNLOAD_PATHS = {
|
||||
TORRENTS: '/download/torrents/',
|
||||
VIDEOS: '/download/videos/',
|
||||
GENERATE_VIDEO: '/download/videos/generate/',
|
||||
WEB_VIDEOS: '/download/web-videos/',
|
||||
HLS_VIDEOS: '/download/streaming-playlists/hls/videos/',
|
||||
USER_EXPORTS: '/download/user-exports/',
|
||||
ORIGINAL_VIDEO_FILE: '/download/original-video-files/'
|
||||
|
@ -1337,7 +1339,7 @@ export {
|
|||
OVERVIEWS,
|
||||
SCHEDULER_INTERVALS_MS,
|
||||
REPEAT_JOBS,
|
||||
STATIC_DOWNLOAD_PATHS,
|
||||
DOWNLOAD_PATHS,
|
||||
MIMETYPES,
|
||||
CRAWL_REQUEST_CONCURRENCY,
|
||||
DEFAULT_AUDIO_RESOLUTION,
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
|
||||
async function up (utils: {
|
||||
transaction: Sequelize.Transaction
|
||||
queryInterface: Sequelize.QueryInterface
|
||||
sequelize: Sequelize.Sequelize
|
||||
}): Promise<void> {
|
||||
const { transaction } = utils
|
||||
|
||||
{
|
||||
await utils.queryInterface.addColumn('videoFile', 'formatFlags', {
|
||||
type: Sequelize.INTEGER,
|
||||
defaultValue: 2, // fragmented
|
||||
allowNull: false
|
||||
}, { transaction })
|
||||
|
||||
// Web videos
|
||||
const query = 'UPDATE "videoFile" SET "formatFlags" = 1 WHERE "videoId" IS NOT NULL'
|
||||
await utils.sequelize.query(query, { transaction })
|
||||
|
||||
await utils.queryInterface.changeColumn('videoFile', 'formatFlags', {
|
||||
type: Sequelize.INTEGER,
|
||||
defaultValue: null,
|
||||
allowNull: false
|
||||
}, { transaction })
|
||||
}
|
||||
|
||||
{
|
||||
await utils.queryInterface.addColumn('videoFile', 'streams', {
|
||||
type: Sequelize.INTEGER,
|
||||
defaultValue: 3, // audio + video
|
||||
allowNull: false
|
||||
}, { transaction })
|
||||
|
||||
// Case where there is only an audio stream
|
||||
const query = 'UPDATE "videoFile" SET "streams" = 2 WHERE "resolution" = 0'
|
||||
await utils.sequelize.query(query, { transaction })
|
||||
|
||||
await utils.queryInterface.changeColumn('videoFile', 'streams', {
|
||||
type: Sequelize.INTEGER,
|
||||
defaultValue: null,
|
||||
allowNull: false
|
||||
}, { transaction })
|
||||
}
|
||||
}
|
||||
|
||||
function down (options) {
|
||||
throw new Error('Not implemented.')
|
||||
}
|
||||
|
||||
export {
|
||||
down, up
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue