// Transcode meta function
// ---------------------------------------------------------------------------
-type TranscodeOptionsType = 'hls' | 'quick-transcode' | 'video' | 'merge-audio' | 'only-audio'
+type TranscodeOptionsType = 'hls' | 'hls-from-ts' | 'quick-transcode' | 'video' | 'merge-audio' | 'only-audio'
interface BaseTranscodeOptions {
type: TranscodeOptionsType
}
}
+interface HLSFromTSTranscodeOptions extends BaseTranscodeOptions {
+ type: 'hls-from-ts'
+
+ hlsPlaylist: {
+ videoFilename: string
+ }
+}
+
interface QuickTranscodeOptions extends BaseTranscodeOptions {
type: 'quick-transcode'
}
type TranscodeOptions =
HLSTranscodeOptions
+ | HLSFromTSTranscodeOptions
| VideoTranscodeOptions
| MergeAudioTranscodeOptions
| OnlyAudioTranscodeOptions
} = {
'quick-transcode': buildQuickTranscodeCommand,
'hls': buildHLSVODCommand,
+ 'hls-from-ts': buildHLSVODFromTSCommand,
'merge-audio': buildAudioMergeCommand,
'only-audio': buildOnlyAudioCommand,
'video': buildx264VODCommand
return command
}
-async function hlsPlaylistToFragmentedMP4 (replayDirectory: string, segmentFiles: string[], outputPath: string) {
- const concatFilePath = join(replayDirectory, 'concat.txt')
-
- function cleaner () {
- remove(concatFilePath)
- .catch(err => logger.error('Cannot remove concat file in %s.', replayDirectory, { err }))
- }
-
- // First concat the ts files to a mp4 file
- const content = segmentFiles.map(f => 'file ' + f)
- .join('\n')
-
- await writeFile(concatFilePath, content + '\n')
-
- const command = getFFmpeg(concatFilePath)
- command.inputOption('-safe 0')
- command.inputOption('-f concat')
-
- command.outputOption('-c:v copy')
- command.audioFilter('aresample=async=1:first_pts=0')
- command.output(outputPath)
-
- return runCommand(command, cleaner)
-}
-
function buildStreamSuffix (base: string, streamNum?: number) {
if (streamNum !== undefined) {
return `${base}:${streamNum}`
generateImageFromVideoFile,
TranscodeOptions,
TranscodeOptionsType,
- transcode,
- hlsPlaylistToFragmentedMP4
+ transcode
}
// ---------------------------------------------------------------------------
return command
}
+function addCommonHLSVODCommandOptions (command: ffmpeg.FfmpegCommand, outputPath: string) {
+ return command.outputOption('-hls_time 4')
+ .outputOption('-hls_list_size 0')
+ .outputOption('-hls_playlist_type vod')
+ .outputOption('-hls_segment_filename ' + outputPath)
+ .outputOption('-hls_segment_type fmp4')
+ .outputOption('-f hls')
+ .outputOption('-hls_flags single_file')
+}
+
async function buildHLSVODCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) {
const videoPath = getHLSVideoPath(options)
else if (options.resolution === VideoResolution.H_NOVIDEO) command = presetOnlyAudio(command)
else command = await buildx264VODCommand(command, options)
- command = command.outputOption('-hls_time 4')
- .outputOption('-hls_list_size 0')
- .outputOption('-hls_playlist_type vod')
- .outputOption('-hls_segment_filename ' + videoPath)
- .outputOption('-hls_segment_type fmp4')
- .outputOption('-f hls')
- .outputOption('-hls_flags single_file')
+ addCommonHLSVODCommandOptions(command, videoPath)
+
+ return command
+}
+
+async function buildHLSVODFromTSCommand (command: ffmpeg.FfmpegCommand, options: HLSFromTSTranscodeOptions) {
+ const videoPath = getHLSVideoPath(options)
+
+ command.inputOption('-safe 0')
+ command.inputOption('-f concat')
+
+ command.outputOption('-c:v copy')
+ command.audioFilter('aresample=async=1:first_pts=0')
+
+ addCommonHLSVODCommandOptions(command, videoPath)
return command
}
async function fixHLSPlaylistIfNeeded (options: TranscodeOptions) {
- if (options.type !== 'hls') return
+ if (options.type !== 'hls' && options.type !== 'hls-from-ts') return
const fileContent = await readFile(options.outputPath)
await writeFile(options.outputPath, newContent)
}
-function getHLSVideoPath (options: HLSTranscodeOptions) {
+function getHLSVideoPath (options: HLSTranscodeOptions | HLSFromTSTranscodeOptions) {
return `${dirname(options.outputPath)}/${options.hlsPlaylist.videoFilename}`
}