Merge pull request #4169 from stanionascu/bdiso-playback

Playback (direct-stream/transcode) of BDISO/BDAV containers
This commit is contained in:
Joshua M. Boniface 2020-12-01 22:36:04 -05:00 committed by GitHub
commit a6199f821b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 117 additions and 279 deletions

View File

@ -12,6 +12,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
@ -81,12 +82,7 @@ namespace Emby.Server.Implementations.MediaEncoder
return false; return false;
} }
if (video.VideoType == VideoType.Iso) if (video.VideoType == VideoType.Dvd)
{
return false;
}
if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
{ {
return false; return false;
} }
@ -140,15 +136,19 @@ namespace Emby.Server.Implementations.MediaEncoder
// Add some time for the first chapter to make sure we don't end up with a black image // Add some time for the first chapter to make sure we don't end up with a black image
var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
var protocol = MediaProtocol.File; var inputPath = video.Path;
var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, null, Array.Empty<string>());
Directory.CreateDirectory(Path.GetDirectoryName(path)); Directory.CreateDirectory(Path.GetDirectoryName(path));
var container = video.Container; var container = video.Container;
var mediaSource = new MediaSourceInfo
{
VideoType = video.VideoType,
IsoType = video.IsoType,
Protocol = video.PathProtocol.Value,
};
var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false); var tempFile = await _encoder.ExtractVideoImage(inputPath, container, mediaSource, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
File.Copy(tempFile, path, true); File.Copy(tempFile, path, true);
try try

View File

@ -762,11 +762,6 @@ namespace Jellyfin.Api.Helpers
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource) private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{ {
if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && _isoManager.CanMount(state.MediaPath))
{
state.IsoMount = await _isoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId)) if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
{ {
var liveStreamResponse = await _mediaSourceManager.OpenLiveStream( var liveStreamResponse = await _mediaSourceManager.OpenLiveStream(

View File

@ -143,26 +143,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The video3 D format.</value> /// <value>The video3 D format.</value>
public Video3DFormat? Video3DFormat { get; set; } public Video3DFormat? Video3DFormat { get; set; }
public string[] GetPlayableStreamFileNames()
{
var videoType = VideoType;
if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.BluRay)
{
videoType = VideoType.BluRay;
}
else if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.Dvd)
{
videoType = VideoType.Dvd;
}
else
{
return Array.Empty<string>();
}
throw new NotImplementedException();
}
/// <summary> /// <summary>
/// Gets or sets the aspect ratio. /// Gets or sets the aspect ratio.
/// </summary> /// </summary>
@ -415,31 +395,6 @@ namespace MediaBrowser.Controller.Entities
return updateType; return updateType;
} }
public static string[] QueryPlayableStreamFiles(string rootPath, VideoType videoType)
{
if (videoType == VideoType.Dvd)
{
return FileSystem.GetFiles(rootPath, new[] { ".vob" }, false, true)
.OrderByDescending(i => i.Length)
.ThenBy(i => i.FullName)
.Take(1)
.Select(i => i.FullName)
.ToArray();
}
if (videoType == VideoType.BluRay)
{
return FileSystem.GetFiles(rootPath, new[] { ".m2ts" }, false, true)
.OrderByDescending(i => i.Length)
.ThenBy(i => i.FullName)
.Take(1)
.Select(i => i.FullName)
.ToArray();
}
return Array.Empty<string>();
}
/// <summary> /// <summary>
/// Gets a value indicating whether [is3 D]. /// Gets a value indicating whether [is3 D].
/// </summary> /// </summary>

View File

@ -390,25 +390,9 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetInputPathArgument(EncodingJobInfo state) public string GetInputPathArgument(EncodingJobInfo state)
{ {
var protocol = state.InputProtocol;
var mediaPath = state.MediaPath ?? string.Empty; var mediaPath = state.MediaPath ?? string.Empty;
string[] inputPath; return _mediaEncoder.GetInputArgument(mediaPath, state.MediaSource);
if (state.IsInputVideo
&& !(state.VideoType == VideoType.Iso && state.IsoMount == null))
{
inputPath = MediaEncoderHelpers.GetInputArgument(
_fileSystem,
mediaPath,
state.IsoMount,
state.PlayableStreamFileNames);
}
else
{
inputPath = new[] { mediaPath };
}
return _mediaEncoder.GetInputArgument(inputPath, protocol);
} }
/// <summary> /// <summary>
@ -2673,18 +2657,10 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
} }
public string GetProbeSizeArgument(int numInputFiles)
=> numInputFiles > 1 ? "-probesize " + _configuration.GetFFmpegProbeSize() : string.Empty;
public string GetAnalyzeDurationArgument(int numInputFiles)
=> numInputFiles > 1 ? "-analyzeduration " + _configuration.GetFFmpegAnalyzeDuration() : string.Empty;
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions) public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions)
{ {
var inputModifier = string.Empty; var inputModifier = string.Empty;
var probeSizeArgument = string.Empty;
var numInputFiles = state.PlayableStreamFileNames.Length > 0 ? state.PlayableStreamFileNames.Length : 1;
var probeSizeArgument = GetProbeSizeArgument(numInputFiles);
string analyzeDurationArgument; string analyzeDurationArgument;
if (state.MediaSource.AnalyzeDurationMs.HasValue) if (state.MediaSource.AnalyzeDurationMs.HasValue)
@ -2693,7 +2669,7 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
else else
{ {
analyzeDurationArgument = GetAnalyzeDurationArgument(numInputFiles); analyzeDurationArgument = string.Empty;
} }
if (!string.IsNullOrEmpty(probeSizeArgument)) if (!string.IsNullOrEmpty(probeSizeArgument))
@ -2877,32 +2853,6 @@ namespace MediaBrowser.Controller.MediaEncoding
state.IsoType = mediaSource.IsoType; state.IsoType = mediaSource.IsoType;
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
{
state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, mediaSource.VideoType.Value).Select(Path.GetFileName).ToArray();
}
else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.BluRay)
{
state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.BluRay).Select(Path.GetFileName).ToArray();
}
else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.Dvd)
{
state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.Dvd).Select(Path.GetFileName).ToArray();
}
else
{
state.PlayableStreamFileNames = Array.Empty<string>();
}
}
else
{
state.PlayableStreamFileNames = Array.Empty<string>();
}
if (mediaSource.Timestamp.HasValue) if (mediaSource.Timestamp.HasValue)
{ {
state.InputTimestamp = mediaSource.Timestamp.Value; state.InputTimestamp = mediaSource.Timestamp.Value;

View File

@ -33,10 +33,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool IsInputVideo { get; set; } public bool IsInputVideo { get; set; }
public IIsoMount IsoMount { get; set; }
public string[] PlayableStreamFileNames { get; set; }
public string OutputAudioCodec { get; set; } public string OutputAudioCodec { get; set; }
public int? OutputVideoBitrate { get; set; } public int? OutputVideoBitrate { get; set; }
@ -313,7 +309,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
TranscodingType = jobType; TranscodingType = jobType;
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
PlayableStreamFileNames = Array.Empty<string>();
SupportedAudioCodecs = Array.Empty<string>(); SupportedAudioCodecs = Array.Empty<string>();
SupportedVideoCodecs = Array.Empty<string>(); SupportedVideoCodecs = Array.Empty<string>();
SupportedSubtitleCodecs = Array.Empty<string>(); SupportedSubtitleCodecs = Array.Empty<string>();

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
@ -61,18 +62,18 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary> /// <summary>
/// Extracts the video image. /// Extracts the video image.
/// </summary> /// </summary>
Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken); Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Extracts the video images on interval. /// Extracts the video images on interval.
/// </summary> /// </summary>
Task ExtractVideoImagesOnInterval( Task ExtractVideoImagesOnInterval(
string[] inputFiles, string inputFile,
string container, string container,
MediaStream videoStream, MediaStream videoStream,
MediaProtocol protocol, MediaSourceInfo mediaSource,
Video3DFormat? threedFormat, Video3DFormat? threedFormat,
TimeSpan interval, TimeSpan interval,
string targetDirectory, string targetDirectory,
@ -91,10 +92,10 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary> /// <summary>
/// Gets the input argument. /// Gets the input argument.
/// </summary> /// </summary>
/// <param name="inputFiles">The input files.</param> /// <param name="inputFile">The input file.</param>
/// <param name="protocol">The protocol.</param> /// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol); string GetInputArgument(string inputFile, MediaSourceInfo mediaSource);
/// <summary> /// <summary>
/// Gets the time parameter. /// Gets the time parameter.

View File

@ -13,38 +13,5 @@ namespace MediaBrowser.Controller.MediaEncoding
/// </summary> /// </summary>
public static class MediaEncoderHelpers public static class MediaEncoderHelpers
{ {
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="fileSystem">The file system.</param>
/// <param name="videoPath">The video path.</param>
/// <param name="isoMount">The iso mount.</param>
/// <param name="playableStreamFileNames">The playable stream file names.</param>
/// <returns>string[].</returns>
public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, IIsoMount isoMount, IReadOnlyCollection<string> playableStreamFileNames)
{
if (playableStreamFileNames.Count > 0)
{
if (isoMount == null)
{
return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames);
}
return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames);
}
return new[] { videoPath };
}
private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames)
{
var allFiles = fileSystem
.GetFilePaths(rootPath, true)
.ToArray();
return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
.Where(f => !string.IsNullOrEmpty(f))
.ToArray();
}
} }
} }

View File

@ -1,9 +1,7 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.MediaEncoding namespace MediaBrowser.Controller.MediaEncoding
{ {
@ -14,14 +12,5 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool ExtractChapters { get; set; } public bool ExtractChapters { get; set; }
public DlnaProfileType MediaType { get; set; } public DlnaProfileType MediaType { get; set; }
public IIsoMount MountedIso { get; set; }
public string[] PlayableStreamFileNames { get; set; }
public MediaInfoRequest()
{
PlayableStreamFileNames = Array.Empty<string>();
}
} }
} }

View File

@ -87,19 +87,19 @@ namespace MediaBrowser.MediaEncoding.Attachments
MediaAttachment mediaAttachment, MediaAttachment mediaAttachment,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false); var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource, mediaAttachment, cancellationToken).ConfigureAwait(false);
return File.OpenRead(attachmentPath); return File.OpenRead(attachmentPath);
} }
private async Task<string> GetReadableFile( private async Task<string> GetReadableFile(
string mediaPath, string mediaPath,
string inputFile, string inputFile,
MediaProtocol protocol, MediaSourceInfo mediaSource,
MediaAttachment mediaAttachment, MediaAttachment mediaAttachment,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var outputPath = GetAttachmentCachePath(mediaPath, protocol, mediaAttachment.Index); var outputPath = GetAttachmentCachePath(mediaPath, mediaSource, mediaAttachment.Index);
await ExtractAttachment(inputFile, protocol, mediaAttachment.Index, outputPath, cancellationToken) await ExtractAttachment(inputFile, mediaSource, mediaAttachment.Index, outputPath, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
return outputPath; return outputPath;
@ -107,7 +107,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
private async Task ExtractAttachment( private async Task ExtractAttachment(
string inputFile, string inputFile,
MediaProtocol protocol, MediaSourceInfo mediaSource,
int attachmentStreamIndex, int attachmentStreamIndex,
string outputPath, string outputPath,
CancellationToken cancellationToken) CancellationToken cancellationToken)
@ -121,7 +121,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
if (!File.Exists(outputPath)) if (!File.Exists(outputPath))
{ {
await ExtractAttachmentInternal( await ExtractAttachmentInternal(
_mediaEncoder.GetInputArgument(new[] { inputFile }, protocol), _mediaEncoder.GetInputArgument(inputFile, mediaSource),
attachmentStreamIndex, attachmentStreamIndex,
outputPath, outputPath,
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
@ -234,10 +234,10 @@ namespace MediaBrowser.MediaEncoding.Attachments
} }
} }
private string GetAttachmentCachePath(string mediaPath, MediaProtocol protocol, int attachmentStreamIndex) private string GetAttachmentCachePath(string mediaPath, MediaSourceInfo mediaSource, int attachmentStreamIndex)
{ {
string filename; string filename;
if (protocol == MediaProtocol.File) if (mediaSource.Protocol == MediaProtocol.File)
{ {
var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture); filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);

View File

@ -1,53 +1,44 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Encoder namespace MediaBrowser.MediaEncoding.Encoder
{ {
public static class EncodingUtils public static class EncodingUtils
{ {
public static string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol) public static string GetInputArgument(string inputPrefix, string inputFile, MediaProtocol protocol)
{ {
if (protocol != MediaProtocol.File) if (protocol != MediaProtocol.File)
{ {
var url = inputFiles[0]; return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", inputFile);
return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", url);
} }
return GetConcatInputArgument(inputFiles); return GetConcatInputArgument(inputFile, inputPrefix);
} }
/// <summary> /// <summary>
/// Gets the concat input argument. /// Gets the concat input argument.
/// </summary> /// </summary>
/// <param name="inputFiles">The input files.</param> /// <param name="inputFile">The input file.</param>
/// <param name="inputPrefix">The input prefix.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private static string GetConcatInputArgument(IReadOnlyList<string> inputFiles) private static string GetConcatInputArgument(string inputFile, string inputPrefix)
{ {
// Get all streams // Get all streams
// If there's more than one we'll need to use the concat command // If there's more than one we'll need to use the concat command
if (inputFiles.Count > 1)
{
var files = string.Join("|", inputFiles.Select(NormalizePath));
return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
}
// Determine the input path for video files // Determine the input path for video files
return GetFileInputArgument(inputFiles[0]); return GetFileInputArgument(inputFile, inputPrefix);
} }
/// <summary> /// <summary>
/// Gets the file input argument. /// Gets the file input argument.
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <param name="inputPrefix">The path prefix.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private static string GetFileInputArgument(string path) private static string GetFileInputArgument(string path, string inputPrefix)
{ {
if (path.IndexOf("://", StringComparison.Ordinal) != -1) if (path.IndexOf("://", StringComparison.Ordinal) != -1)
{ {
@ -57,7 +48,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Quotes are valid path characters in linux and they need to be escaped here with a leading \ // Quotes are valid path characters in linux and they need to be escaped here with a leading \
path = NormalizePath(path); path = NormalizePath(path);
return string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", path); return string.Format(CultureInfo.InvariantCulture, "{1}:\"{0}\"", path, inputPrefix);
} }
/// <summary> /// <summary>

View File

@ -18,6 +18,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Probing; using MediaBrowser.MediaEncoding.Probing;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -83,8 +84,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
_jsonSerializerOptions = JsonDefaults.GetOptions(); _jsonSerializerOptions = JsonDefaults.GetOptions();
} }
private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
/// <inheritdoc /> /// <inheritdoc />
public string EncoderPath => _ffmpegPath; public string EncoderPath => _ffmpegPath;
@ -320,33 +319,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
{ {
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
var inputFile = request.MediaSource.Path;
var inputFiles = MediaEncoderHelpers.GetInputArgument(_fileSystem, request.MediaSource.Path, request.MountedIso, request.PlayableStreamFileNames); string analyzeDuration = string.Empty;
var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
string analyzeDuration;
if (request.MediaSource.AnalyzeDurationMs > 0) if (request.MediaSource.AnalyzeDurationMs > 0)
{ {
analyzeDuration = "-analyzeduration " + analyzeDuration = "-analyzeduration " +
(request.MediaSource.AnalyzeDurationMs * 1000).ToString(); (request.MediaSource.AnalyzeDurationMs * 1000).ToString();
} }
else
{
analyzeDuration = EncodingHelper.GetAnalyzeDurationArgument(inputFiles.Length);
}
probeSize = probeSize + " " + analyzeDuration;
probeSize = probeSize.Trim();
var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File; var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File;
return GetMediaInfoInternal( return GetMediaInfoInternal(
GetInputArgument(inputFiles, request.MediaSource.Protocol), GetInputArgument(inputFile, request.MediaSource),
request.MediaSource.Path, request.MediaSource.Path,
request.MediaSource.Protocol, request.MediaSource.Protocol,
extractChapters, extractChapters,
probeSize, analyzeDuration,
request.MediaType == DlnaProfileType.Audio, request.MediaType == DlnaProfileType.Audio,
request.MediaSource.VideoType, request.MediaSource.VideoType,
forceEnableLogging, forceEnableLogging,
@ -356,12 +346,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary> /// <summary>
/// Gets the input argument. /// Gets the input argument.
/// </summary> /// </summary>
/// <param name="inputFiles">The input files.</param> /// <param name="inputFile">The input file.</param>
/// <param name="protocol">The protocol.</param> /// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType.</exception> /// <exception cref="ArgumentException">Unrecognized InputType.</exception>
public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol) public string GetInputArgument(string inputFile, MediaSourceInfo mediaSource)
=> EncodingUtils.GetInputArgument(inputFiles, protocol); {
var prefix = "file";
if (mediaSource.VideoType == VideoType.BluRay || mediaSource.VideoType == VideoType.Iso)
{
prefix = "bluray";
}
return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol);
}
/// <summary> /// <summary>
/// Gets the media info internal. /// Gets the media info internal.
@ -459,31 +457,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
{ {
return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); var mediaSource = new MediaSourceInfo
{
Protocol = MediaProtocol.File
};
return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, cancellationToken);
} }
public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
{ {
return ExtractImage(inputFiles, container, videoStream, null, protocol, false, threedFormat, offset, cancellationToken); return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, cancellationToken);
} }
public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken) public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken)
{ {
return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken); return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, cancellationToken);
} }
private async Task<string> ExtractImage( private async Task<string> ExtractImage(
string[] inputFiles, string inputFile,
string container, string container,
MediaStream videoStream, MediaStream videoStream,
int? imageStreamIndex, int? imageStreamIndex,
MediaProtocol protocol, MediaSourceInfo mediaSource,
bool isAudio, bool isAudio,
Video3DFormat? threedFormat, Video3DFormat? threedFormat,
TimeSpan? offset, TimeSpan? offset,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var inputArgument = GetInputArgument(inputFiles, protocol); var inputArgument = GetInputArgument(inputFile, mediaSource);
if (isAudio) if (isAudio)
{ {
@ -569,8 +572,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads); var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads);
var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var probeSizeArgument = string.Empty;
var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); var analyzeDurationArgument = string.Empty;
if (!string.IsNullOrWhiteSpace(probeSizeArgument)) if (!string.IsNullOrWhiteSpace(probeSizeArgument))
{ {
@ -679,10 +682,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
public async Task ExtractVideoImagesOnInterval( public async Task ExtractVideoImagesOnInterval(
string[] inputFiles, string inputFile,
string container, string container,
MediaStream videoStream, MediaStream videoStream,
MediaProtocol protocol, MediaSourceInfo mediaSource,
Video3DFormat? threedFormat, Video3DFormat? threedFormat,
TimeSpan interval, TimeSpan interval,
string targetDirectory, string targetDirectory,
@ -690,7 +693,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
int? maxWidth, int? maxWidth,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var inputArgument = GetInputArgument(inputFiles, protocol); var inputArgument = GetInputArgument(inputFile, mediaSource);
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture); var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture);
@ -706,8 +709,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads); var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads);
var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1); var probeSizeArgument = string.Empty;
var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1); var analyzeDurationArgument = string.Empty;
if (!string.IsNullOrWhiteSpace(probeSizeArgument)) if (!string.IsNullOrWhiteSpace(probeSizeArgument))
{ {

View File

@ -168,18 +168,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
MediaStream subtitleStream, MediaStream subtitleStream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
string[] inputFiles; var inputFile = mediaSource.Path;
if (mediaSource.VideoType.HasValue
&& (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd))
{
var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id));
inputFiles = mediaSourceItem.GetPlayableStreamFileNames();
}
else
{
inputFiles = new[] { mediaSource.Path };
}
var protocol = mediaSource.Protocol; var protocol = mediaSource.Protocol;
if (subtitleStream.IsExternal) if (subtitleStream.IsExternal)
@ -187,7 +176,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
protocol = _mediaSourceManager.GetPathProtocol(subtitleStream.Path); protocol = _mediaSourceManager.GetPathProtocol(subtitleStream.Path);
} }
var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, protocol, subtitleStream, cancellationToken).ConfigureAwait(false); var fileInfo = await GetReadableFile(mediaSource.Path, inputFile, mediaSource, subtitleStream, cancellationToken).ConfigureAwait(false);
var stream = await GetSubtitleStream(fileInfo.Path, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false); var stream = await GetSubtitleStream(fileInfo.Path, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false);
@ -220,8 +209,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private async Task<SubtitleInfo> GetReadableFile( private async Task<SubtitleInfo> GetReadableFile(
string mediaPath, string mediaPath,
string[] inputFiles, string inputFile,
MediaProtocol protocol, MediaSourceInfo mediaSource,
MediaStream subtitleStream, MediaStream subtitleStream,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
@ -252,9 +241,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
} }
// Extract // Extract
var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat); var outputPath = GetSubtitleCachePath(mediaPath, mediaSource, subtitleStream.Index, "." + outputFormat);
await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken) await ExtractTextSubtitle(inputFile, mediaSource, subtitleStream.Index, outputCodec, outputPath, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false); return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false);
@ -266,14 +255,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (GetReader(currentFormat, false) == null) if (GetReader(currentFormat, false) == null)
{ {
// Convert // Convert
var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt"); var outputPath = GetSubtitleCachePath(mediaPath, mediaSource, subtitleStream.Index, ".srt");
await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, protocol, outputPath, cancellationToken).ConfigureAwait(false); await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, mediaSource, outputPath, cancellationToken).ConfigureAwait(false);
return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true);
} }
return new SubtitleInfo(subtitleStream.Path, protocol, currentFormat, true); return new SubtitleInfo(subtitleStream.Path, mediaSource.Protocol, currentFormat, true);
} }
private ISubtitleParser GetReader(string format, bool throwIfMissing) private ISubtitleParser GetReader(string format, bool throwIfMissing)
@ -363,11 +352,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// </summary> /// </summary>
/// <param name="inputPath">The input path.</param> /// <param name="inputPath">The input path.</param>
/// <param name="language">The language.</param> /// <param name="language">The language.</param>
/// <param name="inputProtocol">The input protocol.</param> /// <param name="mediaSource">The input mediaSource.</param>
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task ConvertTextSubtitleToSrt(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) private async Task ConvertTextSubtitleToSrt(string inputPath, string language, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken)
{ {
var semaphore = GetLock(outputPath); var semaphore = GetLock(outputPath);
@ -377,7 +366,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
if (!File.Exists(outputPath)) if (!File.Exists(outputPath))
{ {
await ConvertTextSubtitleToSrtInternal(inputPath, language, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false); await ConvertTextSubtitleToSrtInternal(inputPath, language, mediaSource, outputPath, cancellationToken).ConfigureAwait(false);
} }
} }
finally finally
@ -391,14 +380,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// </summary> /// </summary>
/// <param name="inputPath">The input path.</param> /// <param name="inputPath">The input path.</param>
/// <param name="language">The language.</param> /// <param name="language">The language.</param>
/// <param name="inputProtocol">The input protocol.</param> /// <param name="mediaSource">The input mediaSource.</param>
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// The <c>inputPath</c> or <c>outputPath</c> is <c>null</c>. /// The <c>inputPath</c> or <c>outputPath</c> is <c>null</c>.
/// </exception> /// </exception>
private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken)
{ {
if (string.IsNullOrEmpty(inputPath)) if (string.IsNullOrEmpty(inputPath))
{ {
@ -412,7 +401,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, inputProtocol, cancellationToken).ConfigureAwait(false); var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, mediaSource.Protocol, cancellationToken).ConfigureAwait(false);
// FFmpeg automatically convert character encoding when it is UTF-16 // FFmpeg automatically convert character encoding when it is UTF-16
// If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event" // If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event"
@ -515,8 +504,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary> /// <summary>
/// Extracts the text subtitle. /// Extracts the text subtitle.
/// </summary> /// </summary>
/// <param name="inputFiles">The input files.</param> /// <param name="inputFile">The input file.</param>
/// <param name="protocol">The protocol.</param> /// <param name="mediaSource">The mediaSource.</param>
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
/// <param name="outputCodec">The output codec.</param> /// <param name="outputCodec">The output codec.</param>
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
@ -524,8 +513,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="ArgumentException">Must use inputPath list overload.</exception> /// <exception cref="ArgumentException">Must use inputPath list overload.</exception>
private async Task ExtractTextSubtitle( private async Task ExtractTextSubtitle(
string[] inputFiles, string inputFile,
MediaProtocol protocol, MediaSourceInfo mediaSource,
int subtitleStreamIndex, int subtitleStreamIndex,
string outputCodec, string outputCodec,
string outputPath, string outputPath,
@ -540,7 +529,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (!File.Exists(outputPath)) if (!File.Exists(outputPath))
{ {
await ExtractTextSubtitleInternal( await ExtractTextSubtitleInternal(
_mediaEncoder.GetInputArgument(inputFiles, protocol), _mediaEncoder.GetInputArgument(inputFile, mediaSource),
subtitleStreamIndex, subtitleStreamIndex,
outputCodec, outputCodec,
outputPath, outputPath,
@ -706,9 +695,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
} }
} }
private string GetSubtitleCachePath(string mediaPath, MediaProtocol protocol, int subtitleStreamIndex, string outputSubtitleExtension) private string GetSubtitleCachePath(string mediaPath, MediaSourceInfo mediaSource, int subtitleStreamIndex, string outputSubtitleExtension)
{ {
if (protocol == MediaProtocol.File) if (mediaSource.Protocol == MediaProtocol.File)
{ {
var ticksParam = string.Empty; var ticksParam = string.Empty;

View File

@ -150,11 +150,6 @@ namespace MediaBrowser.Providers.MediaInfo
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Video where T : Video
{ {
if (item.VideoType == VideoType.Iso)
{
return _cachedTask;
}
if (item.IsPlaceHolder) if (item.IsPlaceHolder)
{ {
return _cachedTask; return _cachedTask;

View File

@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.MediaInfo
streamFileNames = Array.Empty<string>(); streamFileNames = Array.Empty<string>();
} }
mediaInfoResult = await GetMediaInfo(item, streamFileNames, cancellationToken).ConfigureAwait(false); mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
} }
@ -128,7 +128,6 @@ namespace MediaBrowser.Providers.MediaInfo
private Task<Model.MediaInfo.MediaInfo> GetMediaInfo( private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(
Video item, Video item,
string[] streamFileNames,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -145,7 +144,6 @@ namespace MediaBrowser.Providers.MediaInfo
return _mediaEncoder.GetMediaInfo( return _mediaEncoder.GetMediaInfo(
new MediaInfoRequest new MediaInfoRequest
{ {
PlayableStreamFileNames = streamFileNames,
ExtractChapters = true, ExtractChapters = true,
MediaType = DlnaProfileType.Video, MediaType = DlnaProfileType.Video,
MediaSource = new MediaSourceInfo MediaSource = new MediaSourceInfo

View File

@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
@ -50,7 +51,7 @@ namespace MediaBrowser.Providers.MediaInfo
} }
// No support for this // No support for this
if (video.VideoType == VideoType.Iso || video.VideoType == VideoType.Dvd || video.VideoType == VideoType.BluRay) if (video.VideoType == VideoType.Dvd)
{ {
return Task.FromResult(new DynamicImageResponse { HasImage = false }); return Task.FromResult(new DynamicImageResponse { HasImage = false });
} }
@ -69,11 +70,7 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
var protocol = item.PathProtocol ?? MediaProtocol.File; var protocol = item.PathProtocol ?? MediaProtocol.File;
var inputPath = MediaEncoderHelpers.GetInputArgument( var inputPath = item.Path;
_fileSystem,
item.Path,
null,
item.GetPlayableStreamFileNames());
var mediaStreams = var mediaStreams =
item.GetMediaStreams(); item.GetMediaStreams();
@ -107,7 +104,14 @@ namespace MediaBrowser.Providers.MediaInfo
} }
} }
extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, imageStream, videoIndex, cancellationToken).ConfigureAwait(false); MediaSourceInfo mediaSource = new MediaSourceInfo
{
VideoType = item.VideoType,
IsoType = item.IsoType,
Protocol = item.PathProtocol.Value,
};
extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, mediaSource, imageStream, videoIndex, cancellationToken).ConfigureAwait(false);
} }
else else
{ {
@ -119,8 +123,14 @@ namespace MediaBrowser.Providers.MediaInfo
: TimeSpan.FromSeconds(10); : TimeSpan.FromSeconds(10);
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
var mediaSource = new MediaSourceInfo
{
VideoType = item.VideoType,
IsoType = item.IsoType,
Protocol = item.PathProtocol.Value,
};
extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, videoStream, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, mediaSource, videoStream, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
} }
return new DynamicImageResponse return new DynamicImageResponse