* start to add ffmpeg library * start to hook ffmpeg lib into main app * improvements * more progress * make pipeline builder configurable * more options * move more logic down into ffmpeg lib * ffmpeg lib desired state refactoring * add software scaling and padding * add loudness normalization and software deinterlace * add metadata output options * add setsar filter * use built-in scaling logic * fixes * initial nvidia support * nvidia improvements * support hls mode * print old arguments at debug level * fix package reference * start to add qsv support * formatting * fix tests * add timeout to transcode tests * show successful ffmpeg arguments * add vaapi support * add more software decoders * add experimental transcoder option * call existing ffmpeg process service for unimplemented features * fix nvidia mpeg2video bug * update changelog * ignore some neglected unit tests
86 lines
2.7 KiB
C#
86 lines
2.7 KiB
C#
using LanguageExt;
|
|
|
|
namespace ErsatzTV.FFmpeg.OutputFormat;
|
|
|
|
public class OutputFormatHls : IPipelineStep
|
|
{
|
|
private readonly FrameState _desiredState;
|
|
private readonly Option<string> _mediaFrameRate;
|
|
private readonly string _segmentTemplate;
|
|
private readonly string _playlistPath;
|
|
|
|
public OutputFormatHls(
|
|
FrameState desiredState,
|
|
Option<string> mediaFrameRate,
|
|
string segmentTemplate,
|
|
string playlistPath)
|
|
{
|
|
_desiredState = desiredState;
|
|
_mediaFrameRate = mediaFrameRate;
|
|
_segmentTemplate = segmentTemplate;
|
|
_playlistPath = playlistPath;
|
|
}
|
|
|
|
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
|
|
public IList<string> GlobalOptions => Array.Empty<string>();
|
|
public IList<string> InputOptions => Array.Empty<string>();
|
|
public IList<string> FilterOptions => Array.Empty<string>();
|
|
|
|
public IList<string> OutputOptions
|
|
{
|
|
get
|
|
{
|
|
const int SEGMENT_SECONDS = 4;
|
|
int frameRate = _desiredState.FrameRate.IfNone(GetFrameRateFromMedia);
|
|
|
|
return new List<string>
|
|
{
|
|
"-g", $"{frameRate * SEGMENT_SECONDS}",
|
|
"-keyint_min", $"{frameRate * SEGMENT_SECONDS}",
|
|
"-force_key_frames", $"expr:gte(t,n_forced*{SEGMENT_SECONDS})",
|
|
"-f", "hls",
|
|
"-hls_time", $"{SEGMENT_SECONDS}",
|
|
"-hls_list_size", "0",
|
|
"-segment_list_flags", "+live",
|
|
"-hls_segment_filename",
|
|
_segmentTemplate,
|
|
"-hls_flags", "program_date_time+append_list+discont_start+omit_endlist+independent_segments",
|
|
"-mpegts_flags", "+initial_discontinuity",
|
|
_playlistPath
|
|
};
|
|
}
|
|
}
|
|
|
|
private int GetFrameRateFromMedia()
|
|
{
|
|
var frameRate = 24;
|
|
|
|
foreach (string rFrameRate in _mediaFrameRate)
|
|
{
|
|
if (!int.TryParse(rFrameRate, out int fr))
|
|
{
|
|
string[] split = (rFrameRate ?? string.Empty).Split("/");
|
|
if (int.TryParse(split[0], out int left) && int.TryParse(split[1], out int right))
|
|
{
|
|
fr = (int)Math.Round(left / (double)right);
|
|
}
|
|
else
|
|
{
|
|
fr = 24;
|
|
}
|
|
}
|
|
|
|
frameRate = fr;
|
|
}
|
|
|
|
return frameRate;
|
|
}
|
|
|
|
public FrameState NextState(FrameState currentState) => currentState with
|
|
{
|
|
OutputFormat = OutputFormatKind.Hls,
|
|
HlsPlaylistPath = _playlistPath,
|
|
HlsSegmentTemplate = _segmentTemplate
|
|
};
|
|
}
|