From 313ccaf49567a5d73e7ab6c87aa5b4236faf4105 Mon Sep 17 00:00:00 2001 From: Oleksii Holub Date: Thu, 17 Jan 2019 18:46:35 +0200 Subject: [PATCH] Improve stream selection by not limiting it to mp4 and making higher qualities available Fixes #3 --- ReadMe.md | 6 +++- YoutubeExplode.Converter/IYoutubeConverter.cs | 8 +++--- .../Internal/FfmpegCli.cs | 10 +++++-- YoutubeExplode.Converter/YoutubeConverter.cs | 28 +++++++++---------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index ff751fd..921c3e4 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -31,7 +31,11 @@ If you don't want to add FFmpeg to your git repository, check out how it's downl ##### Download video -This will download highest quality audio and video streams and mux them into one file. If the output format is an audio-only format (e.g. mp3) then the video stream is not downloaded. +This will automatically determine the most fitting audio and video streams, download them, and process them into a single file. Audio streams are prioritized by format then by bitrate. Video streams are prioritized by video quality and framerate, then by format. If the output format is audio-only (e.g. mp3) then the video stream is skipped entirely. + +The approach above will try to select streams to achieve the fastest execution speed while not sacrificing video quality. + +It's recommended to use mp4 as output format where possible, because most videos provide highest quality streams in mp4 and, when the former is not the case, transcoding to mp4 is the fastest compared to other formats. ```c# var converter = new YoutubeConverter(); diff --git a/YoutubeExplode.Converter/IYoutubeConverter.cs b/YoutubeExplode.Converter/IYoutubeConverter.cs index 1656b16..e595355 100644 --- a/YoutubeExplode.Converter/IYoutubeConverter.cs +++ b/YoutubeExplode.Converter/IYoutubeConverter.cs @@ -12,7 +12,7 @@ namespace YoutubeExplode.Converter public interface IYoutubeConverter { /// - /// Downloads given media streams and processes them into a file in the specified format. + /// Downloads given media streams and processes them into a file using specified format. /// Task DownloadAndProcessMediaStreamsAsync(IReadOnlyList mediaStreamInfos, string filePath, string format, @@ -20,21 +20,21 @@ Task DownloadAndProcessMediaStreamsAsync(IReadOnlyList mediaStr CancellationToken cancellationToken = default(CancellationToken)); /// - /// Downloads a video to a file in the specified format using the highest quality media streams from the set. + /// Downloads a video to a file using specified format by selecting media streams from the given set. /// Task DownloadVideoAsync(MediaStreamInfoSet mediaStreamInfoSet, string filePath, string format, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)); /// - /// Downloads a video to a file in the specified format using the highest quality media streams available. + /// Downloads a video to a file using specified format. /// Task DownloadVideoAsync(string videoId, string filePath, string format, IProgress progress = null, CancellationToken cancellationToken = default(CancellationToken)); /// - /// Downloads a video to a file using the highest quality media streams available. + /// Downloads a video to a file. /// Task DownloadVideoAsync(string videoId, string filePath, IProgress progress = null, diff --git a/YoutubeExplode.Converter/Internal/FfmpegCli.cs b/YoutubeExplode.Converter/Internal/FfmpegCli.cs index 5e546be..d882fc6 100644 --- a/YoutubeExplode.Converter/Internal/FfmpegCli.cs +++ b/YoutubeExplode.Converter/Internal/FfmpegCli.cs @@ -31,12 +31,16 @@ public Task ProcessAsync(IReadOnlyList inputFilePaths, // Set output format args.Add($"-f {format}"); - // If transcoding is not required, just copy streams + // Skip transcoding if it's not required if (!transcode) args.Add("-c copy"); - // Set quality - args.Add("-q 0"); + // Optimize mp4 transcoding + if (transcode && string.Equals(format, "mp4", StringComparison.OrdinalIgnoreCase)) + args.Add("-preset ultrafast"); + + // Set max threads + args.Add($"-threads {Environment.ProcessorCount}"); // Disable stdin so that the process will not hang waiting for user input args.Add("-nostdin"); diff --git a/YoutubeExplode.Converter/YoutubeConverter.cs b/YoutubeExplode.Converter/YoutubeConverter.cs index c5fddde..8879095 100644 --- a/YoutubeExplode.Converter/YoutubeConverter.cs +++ b/YoutubeExplode.Converter/YoutubeConverter.cs @@ -54,10 +54,8 @@ public async Task DownloadAndProcessMediaStreamsAsync(IReadOnlyList s.Container.GetFileExtension()) - .Any(f => !string.Equals(f, format, StringComparison.OrdinalIgnoreCase)); + // Determine if transcoding is required for at least one of the streams + var transcode = mediaStreamInfos.Any(s => IsTranscodingRequired(s.Container, format)); // Set up progress-related stuff var progressMixer = progress != null ? new ProgressMixer(progress) : null; @@ -119,24 +117,23 @@ public async Task DownloadVideoAsync(MediaStreamInfoSet mediaStreamInfoSet, stri var mediaStreamInfos = new List(); - // Get best audio stream + // Get best audio stream (priority: transcoding -> bitrate) var audioStreamInfo = mediaStreamInfoSet.Audio - .Where(s => s.Container == Container.Mp4) // only mp4 to make encoding easier - .OrderByDescending(s => s.Bitrate) // order by bitrate - .FirstOrDefault(); // take highest bitrate + .OrderByDescending(s => !IsTranscodingRequired(s.Container, format)) + .ThenByDescending(s => s.Bitrate) + .FirstOrDefault(); // Add to result mediaStreamInfos.Add(audioStreamInfo); - // If needs video - get best video stream + // If needs video - get best video stream (priority: quality -> framerate -> transcoding) if (!IsAudioOnlyFormat(format)) { var videoStreamInfo = mediaStreamInfoSet.Video - .Where(s => s.Container == Container.Mp4) // only mp4 to make encoding easier - .OrderByDescending(s => s.VideoQuality) // order by video quality - .ThenByDescending(s => s.Framerate) // order by framerate - .ThenBy(s => s.Size) // order by size - .FirstOrDefault(); // take smallest size, highest video quality and framerate + .OrderByDescending(s => s.VideoQuality) + .ThenByDescending(s => s.Framerate) + .ThenByDescending(s => !IsTranscodingRequired(s.Container, format)) + .FirstOrDefault(); // Add to result mediaStreamInfos.Add(videoStreamInfo); @@ -191,5 +188,8 @@ public partial class YoutubeConverter private static bool IsAudioOnlyFormat(string format) => AudioOnlyFormats.Contains(format, StringComparer.OrdinalIgnoreCase); + + private static bool IsTranscodingRequired(Container container, string format) + => !string.Equals(container.GetFileExtension(), format, StringComparison.OrdinalIgnoreCase); } } \ No newline at end of file