diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 7054accfad..52221d349e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -82,5 +82,10 @@ namespace MediaBrowser.MediaEncoding.Encoder return null; } + + protected override bool IsVideoEncoder + { + get { return false; } + } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index a350d05774..3c30d7cd80 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -142,6 +142,8 @@ namespace MediaBrowser.MediaEncoding.Encoder throw; } + cancellationToken.Register(() => Cancel(process, encodingJob)); + // MUST read both stdout and stderr asynchronously or a deadlock may occurr process.BeginOutputReadLine(); @@ -157,6 +159,16 @@ namespace MediaBrowser.MediaEncoding.Encoder return encodingJob; } + private void Cancel(Process process, EncodingJob job) + { + Logger.Info("Killing ffmpeg process for {0}", job.OutputFilePath); + + //process.Kill(); + process.StandardInput.WriteLine("q"); + + job.IsCancelled = true; + } + /// /// Processes the exited. /// @@ -169,25 +181,53 @@ namespace MediaBrowser.MediaEncoding.Encoder Logger.Debug("Disposing stream resources"); job.Dispose(); + var isSuccesful = false; + try { - Logger.Info("FFMpeg exited with code {0}", process.ExitCode); + var exitCode = process.ExitCode; + Logger.Info("FFMpeg exited with code {0}", exitCode); + isSuccesful = exitCode == 0; + } + catch + { + Logger.Error("FFMpeg exited with an error."); + } + + if (isSuccesful && !job.IsCancelled) + { + job.TaskCompletionSource.TrySetResult(true); + } + else if (job.IsCancelled) + { try { - job.TaskCompletionSource.TrySetResult(true); + DeleteFiles(job); + } + catch + { + } + try + { + job.TaskCompletionSource.TrySetException(new OperationCanceledException()); } catch { } } - catch + else { - Logger.Error("FFMpeg exited with an error."); - try { - job.TaskCompletionSource.TrySetException(new ApplicationException()); + DeleteFiles(job); + } + catch + { + } + try + { + job.TaskCompletionSource.TrySetException(new ApplicationException("Encoding failed")); } catch { @@ -206,6 +246,11 @@ namespace MediaBrowser.MediaEncoding.Encoder //} } + protected virtual void DeleteFiles(EncodingJob job) + { + File.Delete(job.OutputFilePath); + } + private void OnTranscodeBeginning(EncodingJob job) { job.ReportTranscodingProgress(null, null, null, null); @@ -219,10 +264,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - protected virtual bool IsVideoEncoder - { - get { return false; } - } + protected abstract bool IsVideoEncoder { get; } protected virtual string GetWorkingDirectory(EncodingJobOptions options) { @@ -263,6 +305,12 @@ namespace MediaBrowser.MediaEncoding.Encoder /// System.Int32. protected int GetNumberOfThreads(EncodingJob job, bool isWebm) { + // Only need one thread for sync + if (job.Options.Context == EncodingContext.Static) + { + return 1; + } + if (isWebm) { // Recommended per docs diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index 40ca08c405..c8d121eead 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -18,6 +18,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public class EncodingJob : IDisposable { public bool HasExited { get; internal set; } + public bool IsCancelled { get; internal set; } public Stream LogFileStream { get; set; } public IProgress Progress { get; set; } @@ -399,6 +400,12 @@ namespace MediaBrowser.MediaEncoding.Encoder // job.Framerate = framerate; + if (!percentComplete.HasValue && ticks.HasValue && RunTimeTicks.HasValue) + { + var pct = ticks.Value/RunTimeTicks.Value; + percentComplete = pct*100; + } + if (percentComplete.HasValue) { Progress.Report(percentComplete.Value); diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 36406e3a3f..941649addc 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -173,5 +173,10 @@ namespace MediaBrowser.MediaEncoding.Encoder return null; } + + protected override bool IsVideoEncoder + { + get { return true; } + } } } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 7a363c54a9..b09bfe91fb 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Common.Progress; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; @@ -337,7 +338,9 @@ namespace MediaBrowser.Server.Implementations.Sync cancellationToken.ThrowIfCancellationRequested(); - await ProcessJobItem(item, cancellationToken).ConfigureAwait(false); + var innerProgress = new ActionableProgress(); + + await ProcessJobItem(item, innerProgress, cancellationToken).ConfigureAwait(false); var job = _syncRepo.GetJob(item.JobId); await UpdateJobStatus(job).ConfigureAwait(false); @@ -346,7 +349,7 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private async Task ProcessJobItem(SyncJobItem jobItem, CancellationToken cancellationToken) + private async Task ProcessJobItem(SyncJobItem jobItem, IProgress progress, CancellationToken cancellationToken) { var item = _libraryManager.GetItemById(jobItem.ItemId); if (item == null) @@ -372,12 +375,12 @@ namespace MediaBrowser.Server.Implementations.Sync var video = item as Video; if (video != null) { - await Sync(jobItem, video, deviceProfile, cancellationToken).ConfigureAwait(false); + await Sync(jobItem, video, deviceProfile, progress, cancellationToken).ConfigureAwait(false); } else if (item is Audio) { - await Sync(jobItem, (Audio)item, deviceProfile, cancellationToken).ConfigureAwait(false); + await Sync(jobItem, (Audio)item, deviceProfile, progress, cancellationToken).ConfigureAwait(false); } else if (item is Photo) @@ -391,7 +394,7 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private async Task Sync(SyncJobItem jobItem, Video item, DeviceProfile profile, CancellationToken cancellationToken) + private async Task Sync(SyncJobItem jobItem, Video item, DeviceProfile profile, IProgress progress, CancellationToken cancellationToken) { var options = new VideoOptions { @@ -412,7 +415,26 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.Status = SyncJobItemStatus.Converting; await _syncRepo.Update(jobItem).ConfigureAwait(false); - jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, profile), new Progress(), cancellationToken); + try + { + jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, profile), progress, + cancellationToken); + } + catch (OperationCanceledException) + { + jobItem.Status = SyncJobItemStatus.Queued; + } + catch (Exception ex) + { + jobItem.Status = SyncJobItemStatus.Failed; + _logger.ErrorException("Error during sync transcoding", ex); + } + + if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued) + { + await _syncRepo.Update(jobItem).ConfigureAwait(false); + return; + } } else { @@ -435,7 +457,7 @@ namespace MediaBrowser.Server.Implementations.Sync await _syncRepo.Update(jobItem).ConfigureAwait(false); } - private async Task Sync(SyncJobItem jobItem, Audio item, DeviceProfile profile, CancellationToken cancellationToken) + private async Task Sync(SyncJobItem jobItem, Audio item, DeviceProfile profile, IProgress progress, CancellationToken cancellationToken) { var options = new AudioOptions { @@ -455,8 +477,26 @@ namespace MediaBrowser.Server.Implementations.Sync { jobItem.Status = SyncJobItemStatus.Converting; await _syncRepo.Update(jobItem).ConfigureAwait(false); - - jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, profile), new Progress(), cancellationToken); + + try + { + jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, profile), progress, cancellationToken); + } + catch (OperationCanceledException) + { + jobItem.Status = SyncJobItemStatus.Queued; + } + catch (Exception ex) + { + jobItem.Status = SyncJobItemStatus.Failed; + _logger.ErrorException("Error during sync transcoding", ex); + } + + if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued) + { + await _syncRepo.Update(jobItem).ConfigureAwait(false); + return; + } } else { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs b/MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs index c2925551b7..068261ffd3 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Server.Implementations.Sync public bool IsHidden { - get { return true; } + get { return false; } } public bool IsEnabled diff --git a/SharedVersion.cs b/SharedVersion.cs index d53462b93c..70c7bbf8c4 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5482.1")] +[assembly: AssemblyVersion("3.0.*")] +//[assembly: AssemblyVersion("3.0.5482.1")]