josephnhtam / live-streaming-server-net

A .NET implementation of RTMP live streaming server, supporting HTTP-FLV, WebSocket-FLV, HLS, Kubernetes, cloud storage services integration and more.
https://josephnhtam.github.io/live-streaming-server-net/
MIT License
112 stars 18 forks source link

Multiple resolution support #49

Closed Indoraptor1 closed 1 month ago

Indoraptor1 commented 2 months ago

Hello! Is there any way to make that if i have a stream in 1440p it outputs 1080p and 720p as well, like twitch partners (https://help.twitch.tv/s/article/transcoding-options-faq?language=en_US)

josephnhtam commented 2 months ago

Hi @Indoraptor1

Please refer to Adaptive Bitrate HLS

Indoraptor1 commented 2 months ago

Hello! Thanks! I used the StandaloneDemo, but if i stream in 1440p, how can i view the 720p stream?

code:

using LiveStreamingServerNet.AdminPanelUI;
using LiveStreamingServerNet.Flv.Installer;
using LiveStreamingServerNet.Networking.Helpers;
using LiveStreamingServerNet.Standalone;
using LiveStreamingServerNet.Standalone.Installer;
using System.Net;
using LiveStreamingServerNet.Rtmp;
using LiveStreamingServerNet.StreamProcessor.FFmpeg.Contracts;
using LiveStreamingServerNet.StreamProcessor.Hls.Configurations;
using LiveStreamingServerNet.StreamProcessor.Installer;
using LiveStreamingServerNet.StreamProcessor.Utilities;

namespace LiveStreamingServerNet.StandaloneDemo
{
    public static class Program
    {
        public static async Task Main(string[] args)
        {
            using var liveStreamingServer = CreateLiveStreamingServer();

            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddBackgroundServer(liveStreamingServer, new IPEndPoint(IPAddress.Any, 1935));

            var app = builder.Build();

            app.UseHttpFlv(liveStreamingServer);

            app.MapStandaloneServerApiEndPoints(liveStreamingServer);
            app.UseAdminPanelUI(new AdminPanelUIOptions { BasePath = "/ui", HasHttpFlvPreview = true });

            await app.RunAsync();
        }

        private static ILiveStreamingServer CreateLiveStreamingServer()
        {
            return LiveStreamingServerBuilder.Create()
                .ConfigureRtmpServer(options => options.AddFlv()
                    .AddBandwidthLimiter(100_000_000)
                    .AddStandaloneServices()
                    .AddVideoCodecFilter(builder => builder.Include(VideoCodec.AVC))
                    .AddStreamProcessor()
                    .AddAdaptiveHlsTranscoder(config =>
                    {
                        config.FFmpegPath = ExecutableFinder.FindExecutableFromPATH("ffmpeg")!;
                        config.FFprobePath = ExecutableFinder.FindExecutableFromPATH("ffprobe")!;
                        config.VideoDecodingArguments = "-hwaccel auto -c:v h264_cuvid";
                        config.VideoEncodingArguments = "-c:v h264_nvenc -g 30";

                        config.DownsamplingFilters = new DownsamplingFilter[]{
                            new DownsamplingFilter(
                                Name: "360p",
                                Height: 360,
                                MaxVideoBitrate: "600k",
                                MaxAudioBitrate: "64k"
                            ),

                            new DownsamplingFilter(
                                Name: "480p",
                                Height: 480,
                                MaxVideoBitrate: "1500k",
                                MaxAudioBitrate: "128k"
                            ),

                            new DownsamplingFilter(
                                Name: "720p",
                                Height: 720,
                                MaxVideoBitrate: "3000k",
                                MaxAudioBitrate: "256k"
                            )
                        };
                    })
                )
                .ConfigureLogging(options => options.AddConsole())
                .Build();
        }
    }
    public class HlsOutputPathResolver : IFFmpegOutputPathResolver
    {
        private readonly string _outputPath;

        public HlsOutputPathResolver(string outputPath)
        {
            _outputPath = outputPath;
        }

        public ValueTask<string> ResolveOutputPath(IServiceProvider services, Guid contextIdentifier, string streamPath, IReadOnlyDictionary<string, string> streamArguments)
        {
            return ValueTask.FromResult(Path.Combine(_outputPath, streamPath.Trim('/'), "output.m3u8"));
        }
    }
}

My server has RTX A2000 GPU

josephnhtam commented 2 months ago

Hi @Indoraptor1

If you want to use ASP.NET Core to serve the HLS files, you can refer to the AdaptiveHlsDemo

The output m3u8 master playlist, which provides a list of variant streams of different bitrates and resolutions, will be like this:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=730400,RESOLUTION=640x360,CODECS="avc1.42c01f,mp4a.40.2"
output_360p.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=1790800,RESOLUTION=854x480,CODECS="avc1.42c01f,mp4a.40.2"
output_480p.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=3581600,RESOLUTION=1280x720,CODECS="avc1.42c020,mp4a.40.2"
output_720p.m3u8

If you are using hls.js to play the stream, you may check its Quality Switch Control API to select the 720p stream. For video.js, you may check the plugin videojs-hls-quality-selector. Alternatively, you can directly play the stream with output_720p.m3u8, which is a media playlist.

Indoraptor1 commented 2 months ago

Thanks again! Is there any way to restream a stream? Forexample i stream to this localhost server from obs in 1440p 60fps and i want it to stream 1080p 60fps to youtube and 720p 60fps to twitch and kick.

josephnhtam commented 2 months ago

You may be able to do that with Custom FFmpeg Process

I haven't tested the following script, but you may try to do something like this:

        private static ILiveStreamingServer CreateLiveStreamingServer()
        {
            return LiveStreamingServerBuilder.Create()
                .ConfigureRtmpServer(options => options
                    .AddStreamProcessor()
                    .AddFFmpeg(options =>
                    {
                        options.Name = "restream";
                        options.FFmpegPath = ExecutableFinder.FindExecutableFromPATH("ffmpeg")!;
                        options.FFmpegArguments = "-i {inputPath} -c:v libx264 -vf scale=-2:720 -c:a copy {outputPath}";
                        options.OutputPathResolver = new RestreamPathResolver();
                        options.Condition = new RestreamCondition();
                    })
                )
                .Build();
        }

        private class RestreamPathResolver : IFFmpegOutputPathResolver
        {
            public ValueTask<string> ResolveOutputPath(IServiceProvider services, Guid contextIdentifier, string streamPath, IReadOnlyDictionary<string, string> streamArguments)
            {
                return ValueTask.FromResult("rtmps://exampleYouTubeServer.com:443/stream");
            }
        }

        private class RestreamCondition : IStreamProcessorCondition
        {
            public ValueTask<bool> IsEnabled(IServiceProvider services, string streamPath, IReadOnlyDictionary<string, string> streamArguments)
            {
                return ValueTask.FromResult(true);
            }
        }