Correctly handle RTMP streams without audio

pull/5325/head
Chocobozzz 2022-09-28 10:23:03 +02:00
parent 690bad52e1
commit 1ce4256a65
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
3 changed files with 43 additions and 17 deletions

View File

@ -23,11 +23,24 @@ async function getLiveTranscodingCommand (options: {
fps: number fps: number
bitrate: number bitrate: number
ratio: number ratio: number
hasAudio: boolean
availableEncoders: AvailableEncoders availableEncoders: AvailableEncoders
profile: string profile: string
}) { }) {
const { inputUrl, outPath, resolutions, fps, bitrate, availableEncoders, profile, masterPlaylistName, ratio, latencyMode } = options const {
inputUrl,
outPath,
resolutions,
fps,
bitrate,
availableEncoders,
profile,
masterPlaylistName,
ratio,
latencyMode,
hasAudio
} = options
const command = getFFmpeg(inputUrl, 'live') const command = getFFmpeg(inputUrl, 'live')
@ -47,6 +60,7 @@ async function getLiveTranscodingCommand (options: {
addDefaultEncoderGlobalParams(command) addDefaultEncoderGlobalParams(command)
for (let i = 0; i < resolutions.length; i++) { for (let i = 0; i < resolutions.length; i++) {
const streamMap: string[] = []
const resolution = resolutions[i] const resolution = resolutions[i]
const resolutionFPS = computeFPS(fps, resolution) const resolutionFPS = computeFPS(fps, resolution)
@ -94,9 +108,11 @@ async function getLiveTranscodingCommand (options: {
options: `w=-2:h=${resolution}`, options: `w=-2:h=${resolution}`,
outputs: `vout${resolution}` outputs: `vout${resolution}`
}) })
streamMap.push(`v:${i}`)
} }
{ if (hasAudio) {
const streamType: StreamType = 'audio' const streamType: StreamType = 'audio'
const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType })
if (!builderResult) { if (!builderResult) {
@ -114,9 +130,11 @@ async function getLiveTranscodingCommand (options: {
command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`) command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`)
applyEncoderOptions(command, builderResult.result) applyEncoderOptions(command, builderResult.result)
streamMap.push(`a:${i}`)
} }
varStreamMap.push(`v:${i},a:${i}`) varStreamMap.push(streamMap.join(','))
} }
command.complexFilter(complexFilter) command.complexFilter(complexFilter)

View File

@ -1,4 +1,3 @@
import { readdir, readFile } from 'fs-extra' import { readdir, readFile } from 'fs-extra'
import { createServer, Server } from 'net' import { createServer, Server } from 'net'
import { join } from 'path' import { join } from 'path'
@ -9,7 +8,8 @@ import {
getLiveSegmentTime, getLiveSegmentTime,
getVideoStreamBitrate, getVideoStreamBitrate,
getVideoStreamDimensionsInfo, getVideoStreamDimensionsInfo,
getVideoStreamFPS getVideoStreamFPS,
hasAudioStream
} from '@server/helpers/ffmpeg' } from '@server/helpers/ffmpeg'
import { logger, loggerTagsFactory } from '@server/helpers/logger' import { logger, loggerTagsFactory } from '@server/helpers/logger'
import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config'
@ -20,7 +20,7 @@ import { VideoLiveModel } from '@server/models/video/video-live'
import { VideoLiveSessionModel } from '@server/models/video/video-live-session' import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models' import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models'
import { wait } from '@shared/core-utils' import { pick, wait } from '@shared/core-utils'
import { LiveVideoError, VideoState, VideoStreamingPlaylistType } from '@shared/models' import { LiveVideoError, VideoState, VideoStreamingPlaylistType } from '@shared/models'
import { federateVideoIfNeeded } from '../activitypub/videos' import { federateVideoIfNeeded } from '../activitypub/videos'
import { JobQueue } from '../job-queue' import { JobQueue } from '../job-queue'
@ -232,10 +232,11 @@ class LiveManager {
const now = Date.now() const now = Date.now()
const probe = await ffprobePromise(inputUrl) const probe = await ffprobePromise(inputUrl)
const [ { resolution, ratio }, fps, bitrate ] = await Promise.all([ const [ { resolution, ratio }, fps, bitrate, hasAudio ] = await Promise.all([
getVideoStreamDimensionsInfo(inputUrl, probe), getVideoStreamDimensionsInfo(inputUrl, probe),
getVideoStreamFPS(inputUrl, probe), getVideoStreamFPS(inputUrl, probe),
getVideoStreamBitrate(inputUrl, probe) getVideoStreamBitrate(inputUrl, probe),
hasAudioStream(inputUrl, probe)
]) ])
logger.info( logger.info(
@ -259,26 +260,30 @@ class LiveManager {
return this.runMuxingSession({ return this.runMuxingSession({
sessionId, sessionId,
videoLive, videoLive,
streamingPlaylist, streamingPlaylist,
inputUrl, inputUrl,
fps, fps,
bitrate, bitrate,
ratio, ratio,
allResolutions allResolutions,
hasAudio
}) })
} }
private async runMuxingSession (options: { private async runMuxingSession (options: {
sessionId: string sessionId: string
videoLive: MVideoLiveVideo videoLive: MVideoLiveVideo
streamingPlaylist: MStreamingPlaylistVideo streamingPlaylist: MStreamingPlaylistVideo
inputUrl: string inputUrl: string
fps: number fps: number
bitrate: number bitrate: number
ratio: number ratio: number
allResolutions: number[] allResolutions: number[]
hasAudio: boolean
}) { }) {
const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, ratio, inputUrl } = options const { sessionId, videoLive } = options
const videoUUID = videoLive.Video.uuid const videoUUID = videoLive.Video.uuid
const localLTags = lTags(sessionId, videoUUID) const localLTags = lTags(sessionId, videoUUID)
@ -289,15 +294,11 @@ class LiveManager {
const muxingSession = new MuxingSession({ const muxingSession = new MuxingSession({
context: this.getContext(), context: this.getContext(),
user,
sessionId, sessionId,
videoLive, videoLive,
streamingPlaylist, user,
inputUrl,
bitrate, ...pick(options, [ 'streamingPlaylist', 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ])
ratio,
fps,
allResolutions
}) })
muxingSession.on('master-playlist-created', () => this.publishAndFederateLive(videoLive, localLTags)) muxingSession.on('master-playlist-created', () => this.publishAndFederateLive(videoLive, localLTags))

View File

@ -59,6 +59,8 @@ class MuxingSession extends EventEmitter {
private readonly bitrate: number private readonly bitrate: number
private readonly ratio: number private readonly ratio: number
private readonly hasAudio: boolean
private readonly videoId: number private readonly videoId: number
private readonly videoUUID: string private readonly videoUUID: string
private readonly saveReplay: boolean private readonly saveReplay: boolean
@ -94,6 +96,7 @@ class MuxingSession extends EventEmitter {
bitrate: number bitrate: number
ratio: number ratio: number
allResolutions: number[] allResolutions: number[]
hasAudio: boolean
}) { }) {
super() super()
@ -108,6 +111,8 @@ class MuxingSession extends EventEmitter {
this.bitrate = options.bitrate this.bitrate = options.bitrate
this.ratio = options.ratio this.ratio = options.ratio
this.hasAudio = options.hasAudio
this.allResolutions = options.allResolutions this.allResolutions = options.allResolutions
this.videoId = this.videoLive.Video.id this.videoId = this.videoLive.Video.id
@ -140,6 +145,8 @@ class MuxingSession extends EventEmitter {
bitrate: this.bitrate, bitrate: this.bitrate,
ratio: this.ratio, ratio: this.ratio,
hasAudio: this.hasAudio,
availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
profile: CONFIG.LIVE.TRANSCODING.PROFILE profile: CONFIG.LIVE.TRANSCODING.PROFILE
}) })