import { processImage } from './image-utils'
import { logger } from './logger'
+/**
+ *
+ * Functions that run transcoding/muxing ffmpeg processes
+ * Mainly called by lib/video-transcoding.ts and lib/live-manager.ts
+ *
+ */
+
// ---------------------------------------------------------------------------
// Encoder options
// ---------------------------------------------------------------------------
import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
import { logger } from './logger'
+/**
+ *
+ * Helpers to run ffprobe and extract data from the JSON output
+ *
+ */
+
function ffprobePromise (path: string) {
return new Promise<ffmpeg.FfprobeData>((res, rej) => {
ffmpeg.ffprobe(path, (err, data) => {
+import { logger } from '@server/helpers/logger'
import { getTargetBitrate } from '../../shared/models/videos'
import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils'
-import { ffprobePromise, getAudioStream, getMaxAudioBitrate, getVideoFileBitrate, getVideoStreamFromFile } from '../helpers/ffprobe-utils'
+import {
+ canDoQuickAudioTranscode,
+ ffprobePromise,
+ getAudioStream,
+ getMaxAudioBitrate,
+ getVideoFileBitrate,
+ getVideoStreamFromFile
+} from '../helpers/ffprobe-utils'
import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
-// ---------------------------------------------------------------------------
-// Available encoders profiles
-// ---------------------------------------------------------------------------
+/**
+ *
+ * Available encoders and profiles for the transcoding jobs
+ * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile
+ *
+ */
// Resources:
// * https://slhck.info/video/2017/03/01/rate-control.html
return {
outputOptions: [
- `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}`
+ `-maxrate ${targetBitrate}`,
+ `-bufsize ${targetBitrate * 2}`
]
}
}
}
const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => {
- const parsedAudio = await getAudioStream(input)
+ const probe = await ffprobePromise(input)
+
+ if (await canDoQuickAudioTranscode(input, probe)) {
+ logger.debug('Copy audio stream %s by AAC encoder.', input)
+ return { copy: true, outputOptions: [] }
+ }
+
+ const parsedAudio = await getAudioStream(input, probe)
// We try to reduce the ceiling bitrate by making rough matches of bitrates
// Of course this is far from perfect, but it might save some space in the end
const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
+ logger.debug('Calculating audio bitrate of %s by AAC encoder.', input, { bitrate: parsedAudio.bitrate, audioCodecName })
+
if (bitrate !== undefined && bitrate !== -1) {
return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] }
}
- return { copy: true, outputOptions: [] }
+ return { outputOptions: [ ] }
}
const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
import { availableEncoders } from './video-transcoding-profiles'
/**
- * Optimize the original video file and replace it. The resolution is not changed.
+ *
+ * Functions that run transcoding functions, update the database, cleanup files, create torrent files...
+ * Mainly called by the job queue
+ *
*/
+
+// Optimize the original video file and replace it. The resolution is not changed.
async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) {
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4'
}
}
-/**
- * Transcode the original video file to a lower resolution.
- */
+// Transcode the original video file to a lower resolution.
async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) {
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const extname = '.mp4'
return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath)
}
+// Merge an image with an audio file to create a video
async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) {
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4'
return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
}
+// Generate an HLS playlist from an input file, and update the master playlist
async function generateHlsPlaylist (options: {
video: MVideoWithFile
videoInputPath: string
viewVideo,
wait,
waitJobs,
+ waitUntilLivePublished,
waitUntilLiveStarts,
waitUntilLog
} from '../../../../shared/extra-utils'
})
it('Should enable transcoding with some resolutions and correctly save them', async function () {
- this.timeout(60000)
+ this.timeout(120000)
const resolutions = [ 240, 360, 720 ]
await testVideoResolutions(liveVideoId, resolutions)
await stopFfmpeg(command)
+ await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId)
await waitJobs(servers)
const bitrateLimits = {
- 720: 2800 * 1000,
- 360: 780 * 1000,
- 240: 320 * 1000
+ 720: 3000 * 1000,
+ 360: 1100 * 1000,
+ 240: 600 * 1000
}
for (const server of servers) {
const probe = await ffprobePromise(segmentPath)
const videoStream = await getVideoStreamFromFile(segmentPath, probe)
- console.log(videoStream)
+
expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height])
await makeRawRequest(file.torrentUrl, 200)
await wait(500)
}
-async function waitUntilLiveStarts (url: string, token: string, videoId: number | string) {
+function waitUntilLiveStarts (url: string, token: string, videoId: number | string) {
+ return waitWhileLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE)
+}
+
+function waitUntilLivePublished (url: string, token: string, videoId: number | string) {
+ return waitWhileLiveState(url, token, videoId, VideoState.PUBLISHED)
+}
+
+async function waitWhileLiveState (url: string, token: string, videoId: number | string, state: VideoState) {
let video: VideoDetails
do {
video = res.body
await wait(500)
- } while (video.state.id === VideoState.WAITING_FOR_LIVE)
+ } while (video.state.id === state)
}
async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) {
export {
getLive,
+ waitUntilLivePublished,
updateLive,
waitUntilLiveStarts,
createLive,