Skip to content

Commit

Permalink
Improve stream selection by not limiting it to mp4 and making higher …
Browse files Browse the repository at this point in the history
…qualities available

Fixes Tyrrrz#3
  • Loading branch information
Oleksii Holub authored and Oleksii Holub committed Jan 17, 2019
1 parent f0a2d1c commit 313ccaf
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 22 deletions.
6 changes: 5 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 4 additions & 4 deletions YoutubeExplode.Converter/IYoutubeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@ namespace YoutubeExplode.Converter
public interface IYoutubeConverter
{
/// <summary>
/// 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.
/// </summary>
Task DownloadAndProcessMediaStreamsAsync(IReadOnlyList<MediaStreamInfo> mediaStreamInfos,
string filePath, string format,
IProgress<double> progress = null,
CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// 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.
/// </summary>
Task DownloadVideoAsync(MediaStreamInfoSet mediaStreamInfoSet, string filePath, string format,
IProgress<double> progress = null,
CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// 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.
/// </summary>
Task DownloadVideoAsync(string videoId, string filePath, string format,
IProgress<double> progress = null,
CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// Downloads a video to a file using the highest quality media streams available.
/// Downloads a video to a file.
/// </summary>
Task DownloadVideoAsync(string videoId, string filePath,
IProgress<double> progress = null,
Expand Down
10 changes: 7 additions & 3 deletions YoutubeExplode.Converter/Internal/FfmpegCli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ public Task ProcessAsync(IReadOnlyList<string> 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");
Expand Down
28 changes: 14 additions & 14 deletions YoutubeExplode.Converter/YoutubeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ public async Task DownloadAndProcessMediaStreamsAsync(IReadOnlyList<MediaStreamI
filePath.GuardNotNull(nameof(filePath));
format.GuardNotNull(nameof(format));

// Determine if transcoding is required - if one of the input stream containers doesn't match the output format
var transcode = mediaStreamInfos
.Select(s => 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;
Expand Down Expand Up @@ -119,24 +117,23 @@ public async Task DownloadVideoAsync(MediaStreamInfoSet mediaStreamInfoSet, stri

var mediaStreamInfos = new List<MediaStreamInfo>();

// 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);
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 313ccaf

Please sign in to comment.