From: Chocobozzz Date: Fri, 6 Nov 2020 08:09:36 +0000 (+0100) Subject: Fix audio sync after saving replay X-Git-Tag: v3.0.0-rc.1~405 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;ds=sidebyside;h=3bc68dfd6183078fb56b53e24e74f889c85c4ae0;p=github%2FChocobozzz%2FPeerTube.git Fix audio sync after saving replay hls.js seems to not correctly handle audio gaps with fragmented mp4 (but can with a ts playlist) --- diff --git a/client/package.json b/client/package.json index cb6f77166..152f8445f 100644 --- a/client/package.json +++ b/client/package.json @@ -80,7 +80,7 @@ "extract-text-webpack-plugin": "4.0.0-beta.0", "file-loader": "^6.0.0", "focus-visible": "^5.0.2", - "hls.js": "^0.14.9", + "hls.js": "^0.14.16", "html-loader": "^1.0.0", "html-webpack-plugin": "^4.0.3", "https-browserify": "^1.0.0", diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts index 18b4a2f3c..321f7b09f 100644 --- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts @@ -93,7 +93,7 @@ export class VideoActionsDropdownComponent implements OnChanges { ngOnChanges () { if (this.loaded) { this.loaded = false - this.playlistAdd.reload() + if (this.playlistAdd) this.playlistAdd.reload() } this.buildActions() @@ -277,7 +277,7 @@ export class VideoActionsDropdownComponent implements OnChanges { { label: $localize`Display live information`, handler: ({ video }) => this.showLiveInfoModal(video), - isDisplayed: () => this.isVideoLiveInfoAvailable(), + isDisplayed: () => this.displayOptions.liveInfo && this.isVideoLiveInfoAvailable(), iconName: 'live' }, { diff --git a/client/yarn.lock b/client/yarn.lock index cdafe1458..d3603a4a9 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -5586,10 +5586,10 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -hls.js@^0.14.9: - version "0.14.9" - resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.9.tgz#e85be87d94385ed9947155716578f7c568957d15" - integrity sha512-5j1ONTvIzcIxCtg2eafikFbZ3b/9fDhR6u871LmK7jZ44/Qdc2G4xaSBCwcVK61gz7kTyiobaAhB++2M4J58rQ== +hls.js@^0.14.16: + version "0.14.16" + resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.16.tgz#4ff68a1fa7260a43d316270e9bc7f7bdf93c5731" + integrity sha512-VACiO99DQFBpflR4fI+6GVHUZn35R0SGGQo0XTDZOm2BUXbeuDHTghTC/k2/3wGln6KBmG8/bXIcDIzDsY2UEg== dependencies: eventemitter3 "^4.0.3" url-toolkit "^2.1.6" diff --git a/server.ts b/server.ts index e2ff6327b..6963bbf23 100644 --- a/server.ts +++ b/server.ts @@ -142,14 +142,14 @@ if (isTestInstance()) { } // For the logger -morgan.token('remote-addr', req => { +morgan.token('remote-addr', (req: express.Request) => { if (CONFIG.LOG.ANONYMIZE_IP === true || req.get('DNT') === '1') { return anonymize(req.ip, 16, 16) } return req.ip }) -morgan.token('user-agent', req => { +morgan.token('user-agent', (req: express.Request) => { if (req.get('DNT') === '1') { return useragent.parse(req.get('user-agent')).family } diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 3b794b8a2..7a54b4642 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -1,5 +1,5 @@ import * as ffmpeg from 'fluent-ffmpeg' -import { readFile, remove, writeFile } from 'fs-extra' +import { outputFile, readFile, remove, writeFile } from 'fs-extra' import { dirname, join } from 'path' import { VideoFileMetadata } from '@shared/models/videos/video-file-metadata' import { getMaxBitrate, getTargetBitrate, VideoResolution } from '../../shared/models/videos' @@ -423,8 +423,14 @@ function runLiveMuxing (rtmpUrl: string, outPath: string, deleteSegments: boolea } async function hlsPlaylistToFragmentedMP4 (hlsDirectory: string, segmentFiles: string[], outputPath: string) { - const concatFile = 'concat.txt' - const concatFilePath = join(hlsDirectory, concatFile) + const concatFilePath = join(hlsDirectory, 'concat.txt') + + function cleaner () { + remove(concatFilePath) + .catch(err => logger.error('Cannot remove concat file in %s.', hlsDirectory, { err })) + } + + // First concat the ts files to a mp4 file const content = segmentFiles.map(f => 'file ' + f) .join('\n') @@ -434,25 +440,25 @@ async function hlsPlaylistToFragmentedMP4 (hlsDirectory: string, segmentFiles: s command.inputOption('-safe 0') command.inputOption('-f concat') - command.outputOption('-c copy') + command.outputOption('-c:v copy') + command.audioFilter('aresample=async=1:first_pts=0') command.output(outputPath) - command.run() + return runCommand(command, cleaner) +} - function cleaner () { - remove(concatFilePath) - .catch(err => logger.error('Cannot remove concat file in %s.', hlsDirectory, { err })) - } +async function runCommand (command: ffmpeg.FfmpegCommand, onEnd?: Function) { + command.run() return new Promise((res, rej) => { command.on('error', err => { - cleaner() + if (onEnd) onEnd() rej(err) }) command.on('end', () => { - cleaner() + if (onEnd) onEnd() res() }) @@ -501,7 +507,7 @@ function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string command.outputOption('-hls_flags delete_segments') } - command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%d.ts')}`) + command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%04d.ts')}`) command.outputOption('-master_pl_name master.m3u8') command.outputOption(`-f hls`) diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts index 2b900998a..3892260c4 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts @@ -70,10 +70,6 @@ async function saveLive (video: MVideo, live: MVideoLive) { const segmentFiles = files.filter(f => f.startsWith(shouldStartWith) && f.endsWith('.ts')) await hlsPlaylistToFragmentedMP4(hlsDirectory, segmentFiles, mp4TmpPath) - for (const file of segmentFiles) { - await remove(join(hlsDirectory, file)) - } - if (!duration) { duration = await getDurationFromVideoFile(mp4TmpPath) }