From c63c39ce57dc6a22058da227a11ef1d62f28f627 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 2 Jan 2015 01:12:58 -0500 Subject: sync video transcoding --- MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs | 434 ++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs (limited to 'MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs new file mode 100644 index 000000000..40ca08c40 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -0,0 +1,434 @@ +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.MediaEncoding.Encoder +{ + public class EncodingJob : IDisposable + { + public bool HasExited { get; internal set; } + + public Stream LogFileStream { get; set; } + public IProgress Progress { get; set; } + public TaskCompletionSource TaskCompletionSource; + + public EncodingJobOptions Options { get; set; } + public string InputContainer { get; set; } + public List AllMediaStreams { get; set; } + public MediaStream AudioStream { get; set; } + public MediaStream VideoStream { get; set; } + public MediaStream SubtitleStream { get; set; } + public IIsoMount IsoMount { get; set; } + + public bool ReadInputAtNativeFramerate { get; set; } + public bool IsVideoRequest { get; set; } + public string InputAudioSync { get; set; } + public string InputVideoSync { get; set; } + public string Id { get; set; } + + public string MediaPath { get; set; } + public MediaProtocol InputProtocol { get; set; } + public bool IsInputVideo { get; set; } + public VideoType VideoType { get; set; } + public IsoType? IsoType { get; set; } + public List PlayableStreamFileNames { get; set; } + + public List SupportedAudioCodecs { get; set; } + public Dictionary RemoteHttpHeaders { get; set; } + public TransportStreamTimestamp InputTimestamp { get; set; } + + public bool DeInterlace { get; set; } + public string MimeType { get; set; } + public bool EstimateContentLength { get; set; } + public bool EnableMpegtsM2TsMode { get; set; } + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + public long? EncodingDurationTicks { get; set; } + public string LiveTvStreamId { get; set; } + public long? RunTimeTicks; + + public string ItemType { get; set; } + + public long? InputBitrate { get; set; } + public long? InputFileSize { get; set; } + public string OutputAudioSync = "1"; + public string OutputVideoSync = "vfr"; + + public string GetMimeType(string outputPath) + { + if (!string.IsNullOrEmpty(MimeType)) + { + return MimeType; + } + + return MimeTypes.GetMimeType(outputPath); + } + + private readonly ILogger _logger; + private readonly ILiveTvManager _liveTvManager; + + public EncodingJob(ILogger logger, ILiveTvManager liveTvManager) + { + _logger = logger; + _liveTvManager = liveTvManager; + Id = Guid.NewGuid().ToString("N"); + + RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + _logger = logger; + SupportedAudioCodecs = new List(); + PlayableStreamFileNames = new List(); + RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + AllMediaStreams = new List(); + TaskCompletionSource = new TaskCompletionSource(); + } + + public void Dispose() + { + DisposeLiveStream(); + DisposeLogStream(); + DisposeIsoMount(); + } + + private void DisposeLogStream() + { + if (LogFileStream != null) + { + try + { + LogFileStream.Dispose(); + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing log stream", ex); + } + + LogFileStream = null; + } + } + + private void DisposeIsoMount() + { + if (IsoMount != null) + { + try + { + IsoMount.Dispose(); + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing iso mount", ex); + } + + IsoMount = null; + } + } + + private async void DisposeLiveStream() + { + if (!string.IsNullOrEmpty(LiveTvStreamId)) + { + try + { + await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error closing live tv stream", ex); + } + } + } + + public int InternalSubtitleStreamOffset { get; set; } + + public string OutputFilePath { get; set; } + public string OutputVideoCodec { get; set; } + public string OutputAudioCodec { get; set; } + public int? OutputAudioChannels; + public int? OutputAudioSampleRate; + public int? OutputAudioBitrate; + public int? OutputVideoBitrate; + + public string ActualOutputVideoCodec + { + get + { + var codec = OutputVideoCodec; + + if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + { + var stream = VideoStream; + + if (stream != null) + { + return stream.Codec; + } + + return null; + } + + return codec; + } + } + + public string ActualOutputAudioCodec + { + get + { + var codec = OutputAudioCodec; + + if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + { + var stream = AudioStream; + + if (stream != null) + { + return stream.Codec; + } + + return null; + } + + return codec; + } + } + + public int? TotalOutputBitrate + { + get + { + return (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0); + } + } + + public int? OutputWidth + { + get + { + if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue) + { + var size = new ImageSize + { + Width = VideoStream.Width.Value, + Height = VideoStream.Height.Value + }; + + var newSize = DrawingUtils.Resize(size, + Options.Width, + Options.Height, + Options.MaxWidth, + Options.MaxHeight); + + return Convert.ToInt32(newSize.Width); + } + + if (!IsVideoRequest) + { + return null; + } + + return Options.MaxWidth ?? Options.Width; + } + } + + public int? OutputHeight + { + get + { + if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue) + { + var size = new ImageSize + { + Width = VideoStream.Width.Value, + Height = VideoStream.Height.Value + }; + + var newSize = DrawingUtils.Resize(size, + Options.Width, + Options.Height, + Options.MaxWidth, + Options.MaxHeight); + + return Convert.ToInt32(newSize.Height); + } + + if (!IsVideoRequest) + { + return null; + } + + return Options.MaxHeight ?? Options.Height; + } + } + + /// + /// Predicts the audio sample rate that will be in the output stream + /// + public int? TargetVideoBitDepth + { + get + { + var stream = VideoStream; + return stream == null || !Options.Static ? null : stream.BitDepth; + } + } + + /// + /// Gets the target reference frames. + /// + /// The target reference frames. + public int? TargetRefFrames + { + get + { + var stream = VideoStream; + return stream == null || !Options.Static ? null : stream.RefFrames; + } + } + + /// + /// Predicts the audio sample rate that will be in the output stream + /// + public float? TargetFramerate + { + get + { + var stream = VideoStream; + var requestedFramerate = Options.MaxFramerate ?? Options.Framerate; + + return requestedFramerate.HasValue && !Options.Static + ? requestedFramerate + : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate; + } + } + + /// + /// Predicts the audio sample rate that will be in the output stream + /// + public double? TargetVideoLevel + { + get + { + var stream = VideoStream; + return Options.Level.HasValue && !Options.Static + ? Options.Level.Value + : stream == null ? null : stream.Level; + } + } + + public TransportStreamTimestamp TargetTimestamp + { + get + { + var defaultValue = string.Equals(Options.OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ? + TransportStreamTimestamp.Valid : + TransportStreamTimestamp.None; + + return !Options.Static + ? defaultValue + : InputTimestamp; + } + } + + /// + /// Predicts the audio sample rate that will be in the output stream + /// + public int? TargetPacketLength + { + get + { + var stream = VideoStream; + return !Options.Static + ? null + : stream == null ? null : stream.PacketLength; + } + } + + /// + /// Predicts the audio sample rate that will be in the output stream + /// + public string TargetVideoProfile + { + get + { + var stream = VideoStream; + return !string.IsNullOrEmpty(Options.Profile) && !Options.Static + ? Options.Profile + : stream == null ? null : stream.Profile; + } + } + + public bool? IsTargetAnamorphic + { + get + { + if (Options.Static) + { + return VideoStream == null ? null : VideoStream.IsAnamorphic; + } + + return false; + } + } + + public bool? IsTargetCabac + { + get + { + if (Options.Static) + { + return VideoStream == null ? null : VideoStream.IsCabac; + } + + return true; + } + } + + public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded) + { + var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null; + + // job.Framerate = framerate; + + if (percentComplete.HasValue) + { + Progress.Report(percentComplete.Value); + } + + // job.TranscodingPositionTicks = ticks; + // job.BytesTranscoded = bytesTranscoded; + + var deviceId = Options.DeviceId; + + if (!string.IsNullOrWhiteSpace(deviceId)) + { + var audioCodec = ActualOutputVideoCodec; + var videoCodec = ActualOutputVideoCodec; + + // SessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo + // { + // Bitrate = job.TotalOutputBitrate, + // AudioCodec = audioCodec, + // VideoCodec = videoCodec, + // Container = job.Options.OutputContainer, + // Framerate = framerate, + // CompletionPercentage = percentComplete, + // Width = job.OutputWidth, + // Height = job.OutputHeight, + // AudioChannels = job.OutputAudioChannels, + // IsAudioDirect = string.Equals(job.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase), + // IsVideoDirect = string.Equals(job.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) + // }); + } + } + } +} -- cgit v1.2.3