|
@@ -1,12 +1,10 @@
|
|
|
using MediaBrowser.Common.Extensions;
|
|
|
using MediaBrowser.Common.IO;
|
|
|
-using MediaBrowser.Controller.Channels;
|
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
using MediaBrowser.Controller.Devices;
|
|
|
using MediaBrowser.Controller.Dlna;
|
|
|
using MediaBrowser.Controller.Entities;
|
|
|
using MediaBrowser.Controller.Library;
|
|
|
-using MediaBrowser.Controller.LiveTv;
|
|
|
using MediaBrowser.Controller.MediaEncoding;
|
|
|
using MediaBrowser.Model.Configuration;
|
|
|
using MediaBrowser.Model.Dlna;
|
|
@@ -65,7 +63,6 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
|
protected IFileSystem FileSystem { get; private set; }
|
|
|
|
|
|
- protected ILiveTvManager LiveTvManager { get; private set; }
|
|
|
protected IDlnaManager DlnaManager { get; private set; }
|
|
|
protected IDeviceManager DeviceManager { get; private set; }
|
|
|
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
|
|
@@ -75,14 +72,13 @@ namespace MediaBrowser.Api.Playback
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
|
|
|
/// </summary>
|
|
|
- protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient)
|
|
|
+ protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient)
|
|
|
{
|
|
|
ZipClient = zipClient;
|
|
|
MediaSourceManager = mediaSourceManager;
|
|
|
DeviceManager = deviceManager;
|
|
|
SubtitleEncoder = subtitleEncoder;
|
|
|
DlnaManager = dlnaManager;
|
|
|
- LiveTvManager = liveTvManager;
|
|
|
FileSystem = fileSystem;
|
|
|
ServerConfigurationManager = serverConfig;
|
|
|
UserManager = userManager;
|
|
@@ -95,11 +91,10 @@ namespace MediaBrowser.Api.Playback
|
|
|
/// Gets the command line arguments.
|
|
|
/// </summary>
|
|
|
/// <param name="outputPath">The output path.</param>
|
|
|
- /// <param name="transcodingJobId">The transcoding job identifier.</param>
|
|
|
/// <param name="state">The state.</param>
|
|
|
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
|
|
/// <returns>System.String.</returns>
|
|
|
- protected abstract string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding);
|
|
|
+ protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets the type of the transcoding job.
|
|
@@ -128,10 +123,10 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
|
var outputFileExtension = GetOutputFileExtension(state);
|
|
|
|
|
|
- var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false);
|
|
|
+ var data = GetCommandLineArguments("dummy\\dummy", state, false);
|
|
|
|
|
|
data += "-" + (state.Request.DeviceId ?? string.Empty);
|
|
|
- data += "-" + (state.Request.StreamId ?? string.Empty);
|
|
|
+ data += "-" + (state.Request.PlaySessionId ?? string.Empty);
|
|
|
data += "-" + (state.Request.ClientTime ?? string.Empty);
|
|
|
|
|
|
var dataHash = data.GetMD5().ToString("N");
|
|
@@ -704,7 +699,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
|
if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
|
|
|
{
|
|
|
- var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath);
|
|
|
+ var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.MediaSource.Protocol, CancellationToken.None).Result;
|
|
|
|
|
|
if (!string.IsNullOrEmpty(charenc))
|
|
|
{
|
|
@@ -719,8 +714,10 @@ namespace MediaBrowser.Api.Playback
|
|
|
seconds.ToString(UsCulture));
|
|
|
}
|
|
|
|
|
|
+ var mediaPath = state.MediaPath ?? string.Empty;
|
|
|
+
|
|
|
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
|
|
|
- state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
|
|
|
+ mediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
|
|
|
state.InternalSubtitleStreamOffset.ToString(UsCulture),
|
|
|
seconds.ToString(UsCulture));
|
|
|
}
|
|
@@ -895,12 +892,11 @@ namespace MediaBrowser.Api.Playback
|
|
|
/// <summary>
|
|
|
/// Gets the input argument.
|
|
|
/// </summary>
|
|
|
- /// <param name="transcodingJobId">The transcoding job identifier.</param>
|
|
|
/// <param name="state">The state.</param>
|
|
|
/// <returns>System.String.</returns>
|
|
|
- protected string GetInputArgument(string transcodingJobId, StreamState state)
|
|
|
+ protected string GetInputArgument(StreamState state)
|
|
|
{
|
|
|
- var arg = "-i " + GetInputPathArgument(transcodingJobId, state);
|
|
|
+ var arg = "-i " + GetInputPathArgument(state);
|
|
|
|
|
|
if (state.SubtitleStream != null)
|
|
|
{
|
|
@@ -913,27 +909,18 @@ namespace MediaBrowser.Api.Playback
|
|
|
return arg;
|
|
|
}
|
|
|
|
|
|
- private string GetInputPathArgument(string transcodingJobId, StreamState state)
|
|
|
+ private string GetInputPathArgument(StreamState state)
|
|
|
{
|
|
|
- //if (state.InputProtocol == MediaProtocol.File &&
|
|
|
- // state.RunTimeTicks.HasValue &&
|
|
|
- // state.VideoType == VideoType.VideoFile &&
|
|
|
- // !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
|
- //{
|
|
|
- // if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo)
|
|
|
- // {
|
|
|
- // }
|
|
|
- //}
|
|
|
-
|
|
|
var protocol = state.InputProtocol;
|
|
|
+ var mediaPath = state.MediaPath ?? string.Empty;
|
|
|
|
|
|
- var inputPath = new[] { state.MediaPath };
|
|
|
+ var inputPath = new[] { mediaPath };
|
|
|
|
|
|
if (state.IsInputVideo)
|
|
|
{
|
|
|
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
|
|
|
{
|
|
|
- inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
|
|
|
+ inputPath = MediaEncoderHelpers.GetInputArgument(mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -947,55 +934,25 @@ namespace MediaBrowser.Api.Playback
|
|
|
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
}
|
|
|
|
|
|
- if (string.IsNullOrEmpty(state.MediaPath))
|
|
|
+ if (state.MediaSource.RequiresOpening)
|
|
|
{
|
|
|
- var checkCodecs = false;
|
|
|
-
|
|
|
- if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name))
|
|
|
+ var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
|
|
|
{
|
|
|
- var streamInfo = await LiveTvManager.GetChannelStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
-
|
|
|
- state.LiveTvStreamId = streamInfo.Id;
|
|
|
+ OpenToken = state.MediaSource.OpenToken
|
|
|
|
|
|
- state.MediaPath = streamInfo.Path;
|
|
|
- state.InputProtocol = streamInfo.Protocol;
|
|
|
+ }, false, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
|
|
|
- await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
-
|
|
|
- AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
|
|
|
- checkCodecs = true;
|
|
|
- }
|
|
|
+ AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.VideoRequest, state.RequestedUrl);
|
|
|
|
|
|
- else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) ||
|
|
|
- string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name))
|
|
|
+ if (state.VideoRequest != null)
|
|
|
{
|
|
|
- var streamInfo = await LiveTvManager.GetRecordingStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
-
|
|
|
- state.LiveTvStreamId = streamInfo.Id;
|
|
|
-
|
|
|
- state.MediaPath = streamInfo.Path;
|
|
|
- state.InputProtocol = streamInfo.Protocol;
|
|
|
-
|
|
|
- await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
-
|
|
|
- AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
|
|
|
- checkCodecs = true;
|
|
|
+ TryStreamCopy(state, state.VideoRequest);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- var videoRequest = state.VideoRequest;
|
|
|
-
|
|
|
- if (videoRequest != null && checkCodecs)
|
|
|
- {
|
|
|
- if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
|
|
|
- {
|
|
|
- state.OutputVideoCodec = "copy";
|
|
|
- }
|
|
|
-
|
|
|
- if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
|
|
|
- {
|
|
|
- state.OutputAudioCodec = "copy";
|
|
|
- }
|
|
|
- }
|
|
|
+ if (state.MediaSource.BufferMs.HasValue)
|
|
|
+ {
|
|
|
+ await Task.Delay(state.MediaSource.BufferMs.Value, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1017,7 +974,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
|
|
|
|
|
|
var transcodingId = Guid.NewGuid().ToString("N");
|
|
|
- var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true);
|
|
|
+ var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
|
|
|
|
|
|
if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging)
|
|
|
{
|
|
@@ -1052,7 +1009,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
}
|
|
|
|
|
|
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
|
|
- state.Request.StreamId,
|
|
|
+ state.Request.PlaySessionId,
|
|
|
transcodingId,
|
|
|
TranscodingJobType,
|
|
|
process,
|
|
@@ -1123,7 +1080,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
{
|
|
|
if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo)
|
|
|
{
|
|
|
- transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger);
|
|
|
+ transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
|
|
|
state.TranscodingThrottler.Start();
|
|
|
}
|
|
|
}
|
|
@@ -1554,7 +1511,11 @@ namespace MediaBrowser.Api.Playback
|
|
|
}
|
|
|
else if (i == 21)
|
|
|
{
|
|
|
- request.StreamId = val;
|
|
|
+ request.PlaySessionId = val;
|
|
|
+ }
|
|
|
+ else if (i == 22)
|
|
|
+ {
|
|
|
+ request.LiveStreamId = val;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1644,7 +1605,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
request.AudioCodec = InferAudioCodec(url);
|
|
|
}
|
|
|
|
|
|
- var state = new StreamState(LiveTvManager, Logger)
|
|
|
+ var state = new StreamState(MediaSourceManager, Logger)
|
|
|
{
|
|
|
Request = request,
|
|
|
RequestedUrl = url
|
|
@@ -1658,109 +1619,28 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
|
var item = LibraryManager.GetItemById(request.Id);
|
|
|
|
|
|
- List<MediaStream> mediaStreams = null;
|
|
|
+ state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
|
- state.ItemType = item.GetType().Name;
|
|
|
- state.ItemId = item.Id.ToString("N");
|
|
|
var archivable = item as IArchivable;
|
|
|
state.IsInputArchive = archivable != null && archivable.IsArchive;
|
|
|
|
|
|
- if (item is ILiveTvRecording)
|
|
|
- {
|
|
|
- var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false);
|
|
|
-
|
|
|
- state.VideoType = VideoType.VideoFile;
|
|
|
- state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
|
|
-
|
|
|
- var path = recording.RecordingInfo.Path;
|
|
|
- var mediaUrl = recording.RecordingInfo.Url;
|
|
|
-
|
|
|
- var source = string.IsNullOrEmpty(request.MediaSourceId)
|
|
|
- ? recording.GetMediaSources(false).First()
|
|
|
- : MediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false);
|
|
|
-
|
|
|
- mediaStreams = source.MediaStreams;
|
|
|
-
|
|
|
- // Just to prevent this from being null and causing other methods to fail
|
|
|
- state.MediaPath = string.Empty;
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(path))
|
|
|
- {
|
|
|
- state.MediaPath = path;
|
|
|
- state.InputProtocol = MediaProtocol.File;
|
|
|
- }
|
|
|
- else if (!string.IsNullOrEmpty(mediaUrl))
|
|
|
- {
|
|
|
- state.MediaPath = mediaUrl;
|
|
|
- state.InputProtocol = MediaProtocol.Http;
|
|
|
- }
|
|
|
-
|
|
|
- state.RunTimeTicks = recording.RunTimeTicks;
|
|
|
- state.DeInterlace = true;
|
|
|
- state.OutputAudioSync = "1000";
|
|
|
- state.InputVideoSync = "-1";
|
|
|
- state.InputAudioSync = "1";
|
|
|
- state.InputContainer = recording.Container;
|
|
|
- state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate;
|
|
|
- }
|
|
|
- else if (item is LiveTvChannel)
|
|
|
+ MediaSourceInfo mediaSource = null;
|
|
|
+ if (string.IsNullOrWhiteSpace(request.LiveStreamId))
|
|
|
{
|
|
|
- var channel = LiveTvManager.GetInternalChannel(request.Id);
|
|
|
-
|
|
|
- state.VideoType = VideoType.VideoFile;
|
|
|
- state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
|
|
- mediaStreams = new List<MediaStream>();
|
|
|
-
|
|
|
- state.DeInterlace = true;
|
|
|
+ var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
- // Just to prevent this from being null and causing other methods to fail
|
|
|
- state.MediaPath = string.Empty;
|
|
|
+ mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
|
|
|
+ ? mediaSources.First()
|
|
|
+ : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
|
|
|
-
|
|
|
- var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
|
|
|
- ? mediaSources.First()
|
|
|
- : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
|
|
|
-
|
|
|
- mediaStreams = mediaSource.MediaStreams;
|
|
|
-
|
|
|
- state.MediaPath = mediaSource.Path;
|
|
|
- state.InputProtocol = mediaSource.Protocol;
|
|
|
- state.InputContainer = mediaSource.Container;
|
|
|
- state.InputFileSize = mediaSource.Size;
|
|
|
- state.InputBitrate = mediaSource.Bitrate;
|
|
|
- state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
|
|
|
- state.RunTimeTicks = mediaSource.RunTimeTicks;
|
|
|
- state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
|
|
|
-
|
|
|
- var video = item as Video;
|
|
|
-
|
|
|
- if (video != null)
|
|
|
- {
|
|
|
- state.IsInputVideo = true;
|
|
|
-
|
|
|
- if (mediaSource.VideoType.HasValue)
|
|
|
- {
|
|
|
- state.VideoType = mediaSource.VideoType.Value;
|
|
|
- }
|
|
|
-
|
|
|
- state.IsoType = mediaSource.IsoType;
|
|
|
-
|
|
|
- state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
|
|
|
-
|
|
|
- if (mediaSource.Timestamp.HasValue)
|
|
|
- {
|
|
|
- state.InputTimestamp = mediaSource.Timestamp.Value;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ mediaSource = await MediaSourceManager.GetLiveStream(request.LiveStreamId, cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
|
|
|
var videoRequest = request as VideoStreamRequest;
|
|
|
|
|
|
- AttachMediaStreamInfo(state, mediaStreams, videoRequest, url);
|
|
|
+ AttachMediaSourceInfo(state, mediaSource, videoRequest, url);
|
|
|
|
|
|
var container = Path.GetExtension(state.RequestedUrl);
|
|
|
|
|
@@ -1801,15 +1681,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
|
|
|
if (videoRequest != null)
|
|
|
{
|
|
|
- if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
|
|
|
- {
|
|
|
- state.OutputVideoCodec = "copy";
|
|
|
- }
|
|
|
-
|
|
|
- if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
|
|
|
- {
|
|
|
- state.OutputAudioCodec = "copy";
|
|
|
- }
|
|
|
+ TryStreamCopy(state, videoRequest);
|
|
|
}
|
|
|
|
|
|
state.OutputFilePath = GetOutputFilePath(state);
|
|
@@ -1817,11 +1689,47 @@ namespace MediaBrowser.Api.Playback
|
|
|
return state;
|
|
|
}
|
|
|
|
|
|
- private void AttachMediaStreamInfo(StreamState state,
|
|
|
+ private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
|
|
|
+ {
|
|
|
+ if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
|
|
|
+ {
|
|
|
+ state.OutputVideoCodec = "copy";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
|
|
|
+ {
|
|
|
+ state.OutputAudioCodec = "copy";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AttachMediaSourceInfo(StreamState state,
|
|
|
MediaSourceInfo mediaSource,
|
|
|
VideoStreamRequest videoRequest,
|
|
|
string requestedUrl)
|
|
|
{
|
|
|
+ state.MediaPath = mediaSource.Path;
|
|
|
+ state.InputProtocol = mediaSource.Protocol;
|
|
|
+ state.InputContainer = mediaSource.Container;
|
|
|
+ state.InputFileSize = mediaSource.Size;
|
|
|
+ state.InputBitrate = mediaSource.Bitrate;
|
|
|
+ state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
|
|
|
+ state.RunTimeTicks = mediaSource.RunTimeTicks;
|
|
|
+ state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
|
|
|
+
|
|
|
+ if (mediaSource.VideoType.HasValue)
|
|
|
+ {
|
|
|
+ state.VideoType = mediaSource.VideoType.Value;
|
|
|
+ }
|
|
|
+
|
|
|
+ state.IsoType = mediaSource.IsoType;
|
|
|
+
|
|
|
+ state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
|
|
|
+
|
|
|
+ if (mediaSource.Timestamp.HasValue)
|
|
|
+ {
|
|
|
+ state.InputTimestamp = mediaSource.Timestamp.Value;
|
|
|
+ }
|
|
|
+
|
|
|
state.InputProtocol = mediaSource.Protocol;
|
|
|
state.MediaPath = mediaSource.Path;
|
|
|
state.RunTimeTicks = mediaSource.RunTimeTicks;
|
|
@@ -1830,21 +1738,16 @@ namespace MediaBrowser.Api.Playback
|
|
|
state.InputFileSize = mediaSource.Size;
|
|
|
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
|
|
|
|
|
|
- if (state.ReadInputAtNativeFramerate)
|
|
|
+ if (state.ReadInputAtNativeFramerate ||
|
|
|
+ mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
state.OutputAudioSync = "1000";
|
|
|
state.InputVideoSync = "-1";
|
|
|
state.InputAudioSync = "1";
|
|
|
}
|
|
|
|
|
|
- AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl);
|
|
|
- }
|
|
|
+ var mediaStreams = mediaSource.MediaStreams;
|
|
|
|
|
|
- private void AttachMediaStreamInfo(StreamState state,
|
|
|
- List<MediaStream> mediaStreams,
|
|
|
- VideoStreamRequest videoRequest,
|
|
|
- string requestedUrl)
|
|
|
- {
|
|
|
if (videoRequest != null)
|
|
|
{
|
|
|
if (string.IsNullOrEmpty(videoRequest.VideoCodec))
|
|
@@ -1873,7 +1776,7 @@ namespace MediaBrowser.Api.Playback
|
|
|
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
|
|
|
}
|
|
|
|
|
|
- state.AllMediaStreams = mediaStreams;
|
|
|
+ state.MediaSource = mediaSource;
|
|
|
}
|
|
|
|
|
|
private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
|
|
@@ -2109,7 +2012,6 @@ namespace MediaBrowser.Api.Playback
|
|
|
}
|
|
|
|
|
|
var audioCodec = state.ActualOutputAudioCodec;
|
|
|
-
|
|
|
var videoCodec = state.ActualOutputVideoCodec;
|
|
|
|
|
|
var mediaProfile = state.VideoRequest == null ?
|
|
@@ -2130,7 +2032,9 @@ namespace MediaBrowser.Api.Playback
|
|
|
state.TargetTimestamp,
|
|
|
state.IsTargetAnamorphic,
|
|
|
state.IsTargetCabac,
|
|
|
- state.TargetRefFrames);
|
|
|
+ state.TargetRefFrames,
|
|
|
+ state.TargetVideoStreamCount,
|
|
|
+ state.TargetAudioStreamCount);
|
|
|
|
|
|
if (mediaProfile != null)
|
|
|
{
|
|
@@ -2215,7 +2119,9 @@ namespace MediaBrowser.Api.Playback
|
|
|
state.TranscodeSeekInfo,
|
|
|
state.IsTargetAnamorphic,
|
|
|
state.IsTargetCabac,
|
|
|
- state.TargetRefFrames
|
|
|
+ state.TargetRefFrames,
|
|
|
+ state.TargetVideoStreamCount,
|
|
|
+ state.TargetAudioStreamCount
|
|
|
|
|
|
).FirstOrDefault() ?? string.Empty;
|
|
|
}
|