@ -1,12 +1,7 @@
using MediaBrowser.Common.Plugins ;
using MediaBrowser.Common.Kernel ;
using MediaBrowser.Common.Plugins ;
using MediaBrowser.Model.Plugins ;
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Diagnostics ;
using System.Linq ;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Model.Serialization ;
namespace MediaBrowser.Api
{
@ -15,6 +10,16 @@ namespace MediaBrowser.Api
/// </summary>
public class Plugin : BasePlugin < BasePluginConfiguration >
{
/// <summary>
/// Initializes a new instance of the <see cref="Plugin" /> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
public Plugin ( IKernel kernel , IXmlSerializer xmlSerializer ) : base ( kernel , xmlSerializer )
{
Instance = this ;
}
/// <summary>
/// Gets the name of the plugin
/// </summary>
@ -41,287 +46,5 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The instance.</value>
public static Plugin Instance { get ; private set ; }
/// <summary>
/// Initializes a new instance of the <see cref="Plugin" /> class.
/// </summary>
public Plugin ( )
{
Instance = this ;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void DisposeOnServer ( bool dispose )
{
if ( dispose )
{
var jobCount = ActiveTranscodingJobs . Count ;
Parallel . ForEach ( ActiveTranscodingJobs , OnTranscodeKillTimerStopped ) ;
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if ( jobCount > 0 )
{
Thread . Sleep ( 1000 ) ;
}
}
base . DisposeOnServer ( dispose ) ;
}
/// <summary>
/// The active transcoding jobs
/// </summary>
private readonly List < TranscodingJob > ActiveTranscodingJobs = new List < TranscodingJob > ( ) ;
/// <summary>
/// Called when [transcode beginning].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
/// <param name="process">The process.</param>
public void OnTranscodeBeginning ( string path , TranscodingJobType type , Process process )
{
lock ( ActiveTranscodingJobs )
{
ActiveTranscodingJobs . Add ( new TranscodingJob
{
Type = type ,
Path = path ,
Process = process ,
ActiveRequestCount = 1
} ) ;
}
}
/// <summary>
/// Called when [transcode failed to start].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeFailedToStart ( string path , TranscodingJobType type )
{
lock ( ActiveTranscodingJobs )
{
var job = ActiveTranscodingJobs . First ( j = > j . Type = = type & & j . Path . Equals ( path , StringComparison . OrdinalIgnoreCase ) ) ;
ActiveTranscodingJobs . Remove ( job ) ;
}
}
/// <summary>
/// Determines whether [has active transcoding job] [the specified path].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if [has active transcoding job] [the specified path]; otherwise, <c>false</c>.</returns>
public bool HasActiveTranscodingJob ( string path , TranscodingJobType type )
{
lock ( ActiveTranscodingJobs )
{
return ActiveTranscodingJobs . Any ( j = > j . Type = = type & & j . Path . Equals ( path , StringComparison . OrdinalIgnoreCase ) ) ;
}
}
/// <summary>
/// Called when [transcode begin request].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeBeginRequest ( string path , TranscodingJobType type )
{
lock ( ActiveTranscodingJobs )
{
var job = ActiveTranscodingJobs . FirstOrDefault ( j = > j . Type = = type & & j . Path . Equals ( path , StringComparison . OrdinalIgnoreCase ) ) ;
if ( job = = null )
{
return ;
}
job . ActiveRequestCount + + ;
if ( job . KillTimer ! = null )
{
job . KillTimer . Dispose ( ) ;
job . KillTimer = null ;
}
}
}
/// <summary>
/// Called when [transcode end request].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeEndRequest ( string path , TranscodingJobType type )
{
lock ( ActiveTranscodingJobs )
{
var job = ActiveTranscodingJobs . FirstOrDefault ( j = > j . Type = = type & & j . Path . Equals ( path , StringComparison . OrdinalIgnoreCase ) ) ;
if ( job = = null )
{
return ;
}
job . ActiveRequestCount - - ;
if ( job . ActiveRequestCount = = 0 )
{
var timerDuration = type = = TranscodingJobType . Progressive ? 1000 : 30000 ;
if ( job . KillTimer = = null )
{
job . KillTimer = new Timer ( OnTranscodeKillTimerStopped , job , timerDuration , Timeout . Infinite ) ;
}
else
{
job . KillTimer . Change ( timerDuration , Timeout . Infinite ) ;
}
}
}
}
/// <summary>
/// Called when [transcoding finished].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodingFinished ( string path , TranscodingJobType type )
{
lock ( ActiveTranscodingJobs )
{
var job = ActiveTranscodingJobs . FirstOrDefault ( j = > j . Type = = type & & j . Path . Equals ( path , StringComparison . OrdinalIgnoreCase ) ) ;
if ( job = = null )
{
return ;
}
ActiveTranscodingJobs . Remove ( job ) ;
if ( job . KillTimer ! = null )
{
job . KillTimer . Dispose ( ) ;
job . KillTimer = null ;
}
}
}
/// <summary>
/// Called when [transcode kill timer stopped].
/// </summary>
/// <param name="state">The state.</param>
private void OnTranscodeKillTimerStopped ( object state )
{
var job = ( TranscodingJob ) state ;
lock ( ActiveTranscodingJobs )
{
ActiveTranscodingJobs . Remove ( job ) ;
if ( job . KillTimer ! = null )
{
job . KillTimer . Dispose ( ) ;
job . KillTimer = null ;
}
}
var process = job . Process ;
var hasExited = true ;
try
{
hasExited = process . HasExited ;
}
catch ( Win32Exception ex )
{
Logger . ErrorException ( "Error determining if ffmpeg process has exited for {0}" , ex , job . Path ) ;
}
catch ( InvalidOperationException ex )
{
Logger . ErrorException ( "Error determining if ffmpeg process has exited for {0}" , ex , job . Path ) ;
}
catch ( NotSupportedException ex )
{
Logger . ErrorException ( "Error determining if ffmpeg process has exited for {0}" , ex , job . Path ) ;
}
if ( hasExited )
{
return ;
}
try
{
Logger . Info ( "Killing ffmpeg process for {0}" , job . Path ) ;
process . Kill ( ) ;
}
catch ( Win32Exception ex )
{
Logger . ErrorException ( "Error killing transcoding job for {0}" , ex , job . Path ) ;
}
catch ( InvalidOperationException ex )
{
Logger . ErrorException ( "Error killing transcoding job for {0}" , ex , job . Path ) ;
}
catch ( NotSupportedException ex )
{
Logger . ErrorException ( "Error killing transcoding job for {0}" , ex , job . Path ) ;
}
}
}
/// <summary>
/// Class TranscodingJob
/// </summary>
public class TranscodingJob
{
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string Path { get ; set ; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public TranscodingJobType Type { get ; set ; }
/// <summary>
/// Gets or sets the process.
/// </summary>
/// <value>The process.</value>
public Process Process { get ; set ; }
/// <summary>
/// Gets or sets the active request count.
/// </summary>
/// <value>The active request count.</value>
public int ActiveRequestCount { get ; set ; }
/// <summary>
/// Gets or sets the kill timer.
/// </summary>
/// <value>The kill timer.</value>
public Timer KillTimer { get ; set ; }
}
/// <summary>
/// Enum TranscodingJobType
/// </summary>
public enum TranscodingJobType
{
/// <summary>
/// The progressive
/// </summary>
Progressive ,
/// <summary>
/// The HLS
/// </summary>
Hls
}
}