@ -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 , I LiveTvManager liveTvManager , I DlnaManager dlnaManager , ISubtitleEncoder subtitleEncoder , IDeviceManager deviceManager , IMediaSourceManager mediaSourceManager , IZipClient zipClient )
protected BaseStreamingService ( IServerConfigurationManager serverConfig , IUserManager userManager , ILibraryManager libraryManager , IIsoManager isoManager , IMediaEncoder mediaEncoder , IFileSystem fileSystem , I DlnaManager 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 . Stream Id ? ? string . Empty ) ;
data + = "-" + ( state . Request . PlaySession Id ? ? 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 ( ":/" , "\\:/" ) ,
m ediaPath. 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. M ediaPath } ;
var inputPath = new [ ] { mediaPath } ;
if ( state . IsInputVideo )
{
if ( ! ( state . VideoType = = VideoType . Iso & & state . IsoMount = = null ) )
{
inputPath = MediaEncoderHelpers . GetInputArgument ( state. M ediaPath, 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 ;
await Task . Delay ( 1500 , cancellationTokenSource . Token ) . ConfigureAwait ( false ) ;
} , false , 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 ) )
if ( state . MediaSource . BufferMs . HasValue )
{
state . OutputAudioCodec = "copy" ;
}
}
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 . Stream Id,
state . Request . PlaySession Id,
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 ( LiveTv Manager, Logger )
var state = new StreamState ( MediaSource Manager, 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 )
{
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 ;
// Just to prevent this from being null and causing other methods to fail
state . MediaPath = string . Empty ;
}
else
MediaSourceInfo mediaSource = null ;
if ( string . IsNullOrWhiteSpace ( request . LiveStreamId ) )
{
var mediaSources = await MediaSourceManager . GetPlayackMediaSources ( request . Id , false , cancellationToken ) . ConfigureAwait ( false ) ;
var mediaSource = string . IsNullOrEmpty ( request . MediaSourceId )
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 )
else
{
state . InputTimestamp = mediaSource . Timestamp . Value ;
}
}
mediaSource = await MediaSourceManager . GetLiveStream ( request . LiveStreamId , cancellationToken ) . ConfigureAwait ( false ) ;
}
var videoRequest = request as VideoStreamRequest ;
AttachMediaS treamInfo( state , mediaStreams , videoRequest , url ) ;
AttachMediaSourceInfo ( state , mediaSource , videoRequest , url ) ;
var container = Path . GetExtension ( state . RequestedUrl ) ;
@ -1800,6 +1680,16 @@ namespace MediaBrowser.Api.Playback
ApplyDeviceProfileSettings ( state ) ;
if ( videoRequest ! = null )
{
TryStreamCopy ( state , videoRequest ) ;
}
state . OutputFilePath = GetOutputFilePath ( state ) ;
return state ;
}
private void TryStreamCopy ( StreamState state , VideoStreamRequest videoRequest )
{
if ( state . VideoStream ! = null & & CanStreamCopyVideo ( videoRequest , state . VideoStream ) )
{
@ -1812,16 +1702,34 @@ namespace MediaBrowser.Api.Playback
}
}
state . OutputFilePath = GetOutputFilePath ( state ) ;
return state ;
}
private void AttachMediaStreamInfo ( StreamState state ,
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 ;
}