public static WebMediaInfo GetMediaInfo(MediaSource source) { if (source.MediaType == WebStreamMediaType.TV) { // cache tv files for 10 seconds if (TvCache.ContainsKey(source.Id) && TvCache[source.Id].Item1.AddSeconds(10).CompareTo(DateTime.Now) > 0) { // cache is valid, use it return TvCache[source.Id].Item2; } // get media info and save it to the cache TsBuffer buf = new TsBuffer(source.Id); WebMediaInfo info = GetMediaInfo(buf.GetCurrentFilePath(), true); TvCache[source.Id] = new Tuple<DateTime, WebMediaInfo>(DateTime.Now, info); return info; } else if (!source.Exists) { throw new FileNotFoundException(); } else if (source.SupportsDirectAccess) { using (var impersonator = source.GetImpersonator()) { return GetMediaInfo(source.GetPath(), false); } } else { // not (yet?) supported throw new NotSupportedException(); } }
public static Stream ExtractImage(MediaSource source, int startPosition, int? maxWidth, int? maxHeight) { if (!source.IsLocalFile) { Log.Warn("ExtractImage: Source type={0} id={1} is not supported yet", source.MediaType, source.Id); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented); return null; } // calculate size string ffmpegResize = ""; if (maxWidth != null && maxHeight != null) { try { decimal resolution = MediaInfoWrapper.GetMediaInfo(source).VideoStreams.First().DisplayAspectRatio; ffmpegResize = "-s " + Resolution.Calculate(resolution, new Resolution(maxWidth.Value, maxHeight.Value)).ToString(); } catch (Exception ex) { Log.Error("Error while getting resolution of video stream", ex); } } // get temporary filename string tempDir = Path.Combine(Path.GetTempPath(), "MPExtended", "imagecache"); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } string filename = String.Format("ex_{0}_{1}_{2}_{3}.jpg", source.GetUniqueIdentifier(), startPosition, maxWidth == null ? "null" : maxWidth.ToString(), maxHeight == null ? "null" : maxHeight.ToString()); string tempFile = Path.Combine(tempDir, filename); // maybe it exists if (File.Exists(tempFile)) { return StreamImage(new ImageSource(tempFile)); } // execute it ProcessStartInfo info = new ProcessStartInfo(); info.Arguments = String.Format("-ss {0} -vframes 1 -i \"{1}\" {2} -f image2 {3}", startPosition, source.GetPath(), ffmpegResize, tempFile); info.FileName = Configuration.Streaming.FFMpegPath; info.CreateNoWindow = true; info.UseShellExecute = false; Process proc = new Process(); proc.StartInfo = info; proc.Start(); proc.WaitForExit(); // log when failed if (!File.Exists(tempFile)) { Log.Warn("Failed to extract image to temporary file {0}", tempFile); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError); return null; } return StreamImage(new ImageSource(tempFile)); }
public Resolution CalculateSize(TranscoderProfile profile, MediaSource source, WebMediaInfo info = null) { try { if (!profile.HasVideoStream) return new Resolution(0, 0); if (info == null) { info = MediaInfoHelper.LoadMediaInfoOrSurrogate(source); } if (info.VideoStreams.Count > 0) { return Resolution.Calculate(info.VideoStreams.First().DisplayAspectRatio, profile.MaxOutputWidth, profile.MaxOutputHeight, 2); } } catch (Exception ex) { Log.Warn("Failed to calculate size of output stream", ex); } // default return Resolution.Calculate(MediaInfoHelper.DEFAULT_ASPECT_RATIO, profile.MaxOutputWidth, profile.MaxOutputHeight, 2); }
public Stream Download(string clientDescription, WebMediaType type, int? provider, string itemId, long? position) { // validate source first MediaSource source = new MediaSource(type, provider, itemId); if (!source.Exists) { throw new FileNotFoundException(); } // create context DownloadContext context = new DownloadContext() { ClientDescription = clientDescription, Source = source, StartTime = DateTime.Now, Stream = new ReadTrackingStreamWrapper(source.Retrieve()), MediaInfo = MediaInfoHelper.LoadMediaInfoOrSurrogate(source) // for playerposition view }; // seek to start position if wanted/needed if (position != null && position > 0) { if (context.Stream.CanSeek) { context.Stream.Seek(position.Value, SeekOrigin.Begin); } else { Log.Warn("Download: Cannot seek on stream, failed to set start position to {0}", position); } } // see comment in Streaming.cs:151 string realIp = WCFUtil.GetHeaderValue("forwardedFor", "X-Forwarded-For"); context.ClientIP = realIp == null ? WCFUtil.GetClientIPAddress() : String.Format("{0} (via {1})", realIp, WCFUtil.GetClientIPAddress()); // set headers for downloading WCFUtil.AddHeader("Content-Disposition", "attachment; filename=\"" + source.GetFileInfo().Name + "\""); if (source.MediaType != WebMediaType.TV) WCFUtil.SetContentLength(source.GetFileInfo().Size); // FIXME: there has to be a better way to do this string mime = MIME.GetFromFilename(source.GetFileInfo().Name); if (mime != null) { WCFUtil.SetContentType(mime); } // finally, save the context and return runningDownloads.Add(context); return context.Stream; }
public static WebMediaInfo GetMediaInfo(MediaSource source) { // we can't use our persistent cache for TV unfortunately, but we do cache them in memory for 60 seconds if (source.MediaType == WebMediaType.TV) { if (tvCache.ContainsKey(source.Id) && DateTime.Now - tvCache[source.Id].Item1 > TimeSpan.FromSeconds(60)) { return tvCache[source.Id].Item2; } // save it to our memory cache for a while TsBuffer buf = new TsBuffer(source.Id); string path = buf.GetCurrentFilePath(); Log.Debug("Using path {0} from TS buffer {1} as source for {2}", path, source.Id, source.GetDebugName()); WebMediaInfo info = DoLoadMediaInfo(buf.GetCurrentFilePath(), true); tvCache[source.Id] = new Tuple<DateTime, WebMediaInfo>(DateTime.Now, info); return info; } // load this item from persistent disk cache, if possible if (persistentCache.HasForSource(source)) { return persistentCache.GetForSource(source); } // some checks that only matter when we are actually going to load it from disk if (!source.Exists) { Log.Warn("Trying to load mediainfo for {0}, which doesn't seem to exist", source.GetDebugName()); return null; } else if (!source.SupportsDirectAccess) { // not (yet?) supported Log.Warn("Loading mediainfo for non-direct access source {0} isn't supported yet", source.GetDebugName()); return null; } // actually load it WebMediaInfo outInfo; using (NetworkShareImpersonator impersonator = new NetworkShareImpersonator(source.NeedsImpersonation)) { outInfo = DoLoadMediaInfo(source.GetPath(), false); } if (outInfo != null) { persistentCache.Save(source, outInfo); } return outInfo; }
public static Stream ExtractImage(MediaSource source, long position, int? maxWidth, int? maxHeight, string borders, string format) { if (!source.Exists) { Log.Warn("ExtractImage: Source {0} (resolved to path {1}) doesn't exists", source.GetDebugName(), source.GetPath()); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound); return Stream.Null; } if (!source.SupportsDirectAccess) { Log.Warn("ExtractImage: Extracting images from remote sources isn't supported yet", source.MediaType, source.Id); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented); return Stream.Null; } // get temporary filename string filename = String.Format("extract_{0}_{1}.jpg", source.GetUniqueIdentifier(), position); string tempFile = cache.GetPath(filename); // maybe it exists in cache, return that then if (cache.Contains(filename)) { return StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format); } // execute it ProcessStartInfo info = new ProcessStartInfo(); using (var impersonator = source.GetImpersonator()) { info.Arguments = String.Format("-ss {0} -i \"{1}\" -vframes 1 -f image2 {2}", position, source.GetPath(), tempFile); info.FileName = Configuration.StreamingProfiles.FFMpegPath; info.CreateNoWindow = true; info.UseShellExecute = false; Process proc = new Process(); proc.StartInfo = info; proc.Start(); proc.WaitForExit(); } // log when failed if (!File.Exists(tempFile)) { Log.Warn("Failed to extract image to temporary file {0} with command {1}", tempFile, info.Arguments); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError); return Stream.Null; } return StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format); }
public static WebMediaInfo GetMediaInfo(MediaSource source) { // we can't cache it for TV, unfortunately if (source.MediaType == WebStreamMediaType.TV) { // cache tv files for 60 seconds if (tvCache.ContainsKey(source.Id) && DateTime.Now - tvCache[source.Id].Item1 > TimeSpan.FromSeconds(60)) { // cache is valid, use it return tvCache[source.Id].Item2; } // get media info and save it to the cache TsBuffer buf = new TsBuffer(source.Id); string path = buf.GetCurrentFilePath(); Log.Debug("Using path {0} from TS buffer {1} as source for {2}", path, source.Id, source.GetDebugName()); WebMediaInfo info = DoLoadMediaInfo(buf.GetCurrentFilePath(), true); tvCache[source.Id] = new Tuple<DateTime, WebMediaInfo>(DateTime.Now, info); return info; } // load this item from cache, if possible if (persistentCache.HasForSource(source)) { return persistentCache.GetForSource(source); } // some checks that only matter when we are actually going to load it from disk if (!source.Exists) { Log.Warn("Trying to load mediainfo for {0}, which doesn't seem to exist", source.GetDebugName()); throw new FileNotFoundException(); } else if (!source.SupportsDirectAccess) { // not (yet?) supported Log.Warn("Loading mediainfo for non-direct access source {0} isn't supported yet", source.GetDebugName()); throw new NotSupportedException(); } // actually load it WebMediaInfo outInfo; using (var impersonator = source.GetImpersonator()) { outInfo = DoLoadMediaInfo(source.GetPath(), false); } persistentCache.Save(source, outInfo); return outInfo; }
public static decimal DEFAULT_ASPECT_RATIO = (decimal)16 / 9; // most new material is 16:9 these days #endregion Fields #region Methods public static WebMediaInfo LoadMediaInfoOrSurrogate(MediaSource source) { WebMediaInfo info; try { info = MediaInfoWrapper.GetMediaInfo(source); if (info != null) { return info; } } catch (Exception ex) { Log.Warn(String.Format("Failed to load MediaInfo for {0}", source.GetDebugName()), ex); } WebMediaInfo surr = new WebMediaInfo(); surr.Duration = 0; surr.SubtitleStreams = new List<WebSubtitleStream>(); surr.AudioStreams = new List<WebAudioStream>() { new WebAudioStream() { Channels = 2, Codec = "Unknown", ID = 1, Index = 0, Language = "und", // yes, that's valid ISO 639 (I think) LanguageFull = "Unknown", } }; surr.VideoStreams = new List<WebVideoStream>() { new WebVideoStream() { Codec = "Unknown", DisplayAspectRatio = DEFAULT_ASPECT_RATIO, DisplayAspectRatioString = "16:9", ID = 2, Index = 0, Height = 1280, // gives this any problems? Width = 720 } }; return surr; }
public static WebMediaInfo GetMediaInfo(MediaSource source) { // Timeshiftings are a special case, as they can't be cached and need an additional path resolving step if (source.MediaType == WebMediaType.TV) { if (tvCache.ContainsKey(source.Id) && DateTime.Now - tvCache[source.Id].Item1 > TimeSpan.FromSeconds(60)) return tvCache[source.Id].Item2; TsBuffer tsBuffer = new TsBuffer(source.Id); Log.Debug("Using path {0} from TS buffer {1} as source for {2}", tsBuffer.GetCurrentFilePath(), source.Id, source.GetDebugName()); WebMediaInfo info = LoadMediaInfo(tsBuffer.GetCurrentFilePath()); tvCache[source.Id] = new Tuple<DateTime, WebMediaInfo>(DateTime.Now, info); return info; } using (var context = source.CreateNetworkContext()) { // verify the file actually exists and is accessible over the local file system if (!source.Exists) { Log.Warn("Trying to load MediaInfo for {0}, which does not exist or is inaccessible", source.GetDebugName()); return null; } else if (!source.SupportsDirectAccess) { Log.Warn("Loading MediaInfo for non-direct access source {0} isn't supported yet", source.GetDebugName()); return null; } // if we got the file in the cache, return it if we have it and the file hasn't been changed var fileInfo = source.GetFileInfo(); if (source.MediaType != WebMediaType.TV && persistentCache.HasForSource(source)) { var cachedItem = persistentCache.GetForSource(source); if (cachedItem.Size == fileInfo.Size && cachedItem.CachedDate >= fileInfo.LastModifiedTime) return cachedItem.Info; } var info = LoadMediaInfo(context.RewritePath(source.GetPath())); if (info != null) persistentCache.Save(source, new CachedInfoWrapper(info, fileInfo)); return info; } }
public Resolution CalculateSize(TranscoderProfile profile, MediaSource source) { if (!profile.HasVideoStream) return new Resolution(0, 0); decimal aspect; if (source.MediaType == WebStreamMediaType.TV || profile == null) { // FIXME: we might want to support TV with other aspect ratios aspect = (decimal)16 / 9; } else { WebMediaInfo info = MediaInfoWrapper.GetMediaInfo(source); aspect = info.VideoStreams.First().DisplayAspectRatio; } return Resolution.Calculate(aspect, new Resolution(profile.MaxOutputWidth, profile.MaxOutputHeight), 2); }
public Resolution CalculateSize(TranscoderProfile profile, MediaSource source, WebMediaInfo info = null) { if (!profile.HasVideoStream) return new Resolution(0, 0); decimal aspect = (decimal)16 / 9; // the default aspect ratio if (source.MediaType != WebStreamMediaType.TV && profile != null) { if (info == null) { info = MediaInfoWrapper.GetMediaInfo(source); } if (info.VideoStreams.Count > 0) { aspect = info.VideoStreams.First().DisplayAspectRatio; } } return Resolution.Calculate(aspect, profile.MaxOutputWidth, profile.MaxOutputHeight, 2); }
public static WebMediaInfo LoadMediaInfoOrSurrogate(MediaSource source) { WebMediaInfo info = MediaInfoWrapper.GetMediaInfo(source); if (info != null) { return info; } WebMediaInfo surr = new WebMediaInfo(); surr.Duration = 0; surr.SubtitleStreams = new List<WebSubtitleStream>(); surr.AudioStreams = new List<WebAudioStream>() { new WebAudioStream() { Channels = 2, Codec = "Unknown", ID = 1, Index = 0, Language = "und", // yes, that's valid ISO 639 (I think) LanguageFull = "Unknown", } }; surr.VideoStreams = new List<WebVideoStream>() { new WebVideoStream() { Codec = "Unknown", DisplayAspectRatio = 16 / 9, // this class is primarily used for TV data and that's mostly 16:9 these days afaik DisplayAspectRatioString = "16:9", ID = 2, Index = 0, Height = 1280, // gives this any problems? Width = 720 } }; return surr; }
public void EndStream(MediaSource source, bool force = false) { // generate identifier string identifier = GetIdentifierFromMediaSource(source); // ignore if not registered if (!enabled || !streams.ContainsKey(identifier)) { return; } if (streams[identifier].TranscodingInfo != null && streams[identifier].TranscodingInfo.Value != null) { int progress = CalculateWatchPosition(identifier); if (progress >= 95) { Log.Debug("WatchSharing: seeing {0}% as finished for {1}", progress, identifier); // send the finished event in a background thread ThreadManager.Start("FinishWatching", delegate() { if (streams[identifier].Context.Source.MediaType == WebStreamMediaType.TVEpisode) { service.FinishEpisode((WebTVEpisodeDetailed)streams[identifier].MediaDescriptor); } else if (streams[identifier].Context.Source.MediaType == WebStreamMediaType.Movie) { service.FinishMovie((WebMovieDetailed)streams[identifier].MediaDescriptor); } // kill it streams[identifier].BackgroundThread.Abort(); ThreadManager.Remove(streams[identifier].BackgroundThread); streams.Remove(identifier); Log.Debug("WatchSharing: finished handling {0}", identifier); }); return; } } // cancel it if (!force) { Log.Debug("WatchSharing: canceling stream {0}", identifier); streams[identifier].Canceled = true; } else { // definitely cancel it Log.Debug("WatchSharing: killing stream {0} because of forced EndStream", identifier); lock (service) { if (streams[identifier].Context.Source.MediaType == WebStreamMediaType.TVEpisode) { service.CancelWatchingEpisode((WebTVEpisodeDetailed)streams[identifier].MediaDescriptor); } else if (streams[identifier].Context.Source.MediaType == WebStreamMediaType.Movie) { service.CancelWatchingMovie((WebMovieDetailed)streams[identifier].MediaDescriptor); } } } }
public bool InitStream(string identifier, string clientDescription, MediaSource source) { if (!source.Exists) { Log.Warn("Tried to start stream for non-existing file {0}", source.GetDebugName()); return false; } ActiveStream stream = new ActiveStream(); stream.Identifier = identifier; stream.ClientDescription = clientDescription; stream.ClientIP = WCFUtil.GetClientIPAddress(); stream.StartTime = DateTime.Now; stream.Context = new StreamContext(); stream.Context.Source = source; stream.Context.IsTv = source.MediaType == WebStreamMediaType.TV; lock (Streams) { Streams[identifier] = stream; } return true; }
public WebItemSupportStatus GetItemSupportStatus(WebMediaType type, int? provider, string itemId) { // check if we actually now about this file MediaSource source = new MediaSource(type, provider, itemId); string path = source.GetPath(); if (path == null || path.Length == 0) { return new WebItemSupportStatus(false, "Cannot resolve item to a path"); } // some checks based upon the file info. apparantly people have broken files in their connections var fileinfo = source.GetFileInfo(); if (!fileinfo.Exists) { // add a special warning message for files that are on a network drive, as this often causes problems Uri uri = new Uri(path); if (uri.IsUnc && !NetworkInformation.IsLocalAddress(uri.Host)) { return new WebItemSupportStatus(false, "File is on an inaccessible network share"); } return new WebItemSupportStatus(false, "File does not exists or is inaccessible"); } if (fileinfo.Size == 0) { return new WebItemSupportStatus(false, "This file has a size of 0KB"); } // we don't support some things yet if (path.EndsWith(".IFO")) { return new WebItemSupportStatus(false, "Streaming DVD files is not supported"); } // while corrupt files may work, it's probably a better idea to warn early. check for a valid file using mediainfo if (MediaInfo.MediaInfoWrapper.GetMediaInfo(source) == null) { return new WebItemSupportStatus(false, "This file might be corrupt"); } return new WebItemSupportStatus() { Supported = true }; }
public static Stream ExtractImage(MediaSource source, long position, string format) { return Images.ExtractImage(source, position, null, null, null, format); }
public static Stream ExtractImage(MediaSource source, long position, int? maxWidth, int? maxHeight, string borders, string format) { if (!source.Exists) { Log.Warn("ExtractImage: Source {0} (resolved to path {1}) doesn't exists", source.GetDebugName(), source.GetPath()); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound); return Stream.Null; } if (!source.SupportsDirectAccess) { Log.Warn("ExtractImage: Extracting images from remote sources isn't supported yet", source.MediaType, source.Id); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented); return Stream.Null; } // get temporary filename string filename = String.Format("extract_{0}_{1}.jpg", source.GetUniqueIdentifier(), position); string tempFile = cache.GetPath(filename); // maybe it exists in cache, return that then if (cache.Contains(filename)) { // if source is newer, cached image needs to be recreated if (source.GetFileInfo().LastModifiedTime > cache.GetLastModifiedTime(tempFile)) { cache.Invalidate(filename); } else { return StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format); } } // We need to impersonate to access the network drive and check if the thumbnail already exists. However, because // impersonation is lost when starting a process, we can't start ffmpeg from in here. We need to check whether the // file is accessible from outside the impersonation context again, and start the ffmpeg process as a different user // if that is the case. string fullPath; using (var context = source.CreateNetworkContext()) { fullPath = context.RewritePath(source.GetPath()); // stream a pre-existing thumbnail, if possible if (source.MediaType == WebMediaType.Recording && Path.GetExtension(fullPath).ToLower() == ".ts") { var thumbnailFileInfo = new FileInfo(Path.ChangeExtension(fullPath, ".jpg")); if (thumbnailFileInfo.Exists && thumbnailFileInfo.Length > 0) return StreamPostprocessedImage(new ImageMediaSource(thumbnailFileInfo.FullName), maxWidth, maxHeight, borders, format); } } // finally, extract the image with ffmpeg if everything else has failed bool extractResult = ExecuteFFMpegExtraction(source, fullPath, position, tempFile); if (!extractResult) { WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError); return Stream.Null; } return StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format); }
private string GetIdentifierFromMediaSource(MediaSource source) { return(Enum.GetName(typeof(WebMediaType), source.MediaType) + "_" + source.Id); }
private static bool ExecuteFFMpegExtraction(MediaSource source, string path, long position, string tempFile) { var info = new ProcessStartInfo(); info.Arguments = String.Format("-ss {0} -i \"{1}\" -vframes 1 -vf \"yadif,scale=ih*dar:ih\" -f image2 \"{2}\"", position, path, tempFile); info.FileName = Configuration.StreamingProfiles.FFMpegPath; info.CreateNoWindow = true; info.UseShellExecute = false; info.RedirectStandardError = Log.IsEnabled(LogLevel.Trace); info.RedirectStandardOutput = Log.IsEnabled(LogLevel.Trace); TranscoderProcess proc = new TranscoderProcess(); proc.StartInfo = info; if (source.NeedsImpersonation) proc.StartAsUser(Configuration.Services.NetworkImpersonation.Domain, Configuration.Services.NetworkImpersonation.Username, Configuration.Services.NetworkImpersonation.GetPassword()); else proc.Start(); if (Log.IsEnabled(LogLevel.Trace)) { Log.Trace("ExtractImage: executing path={0} arguments={1}", info.FileName, info.Arguments); StreamCopy.AsyncStreamRead(proc.StandardError, l => Log.Trace("ExtractImage: stderr: {0}", l)); StreamCopy.AsyncStreamRead(proc.StandardOutput, l => Log.Trace("ExtractImage: stdout: {0}", l)); } proc.WaitForExit(); if (!File.Exists(tempFile)) { Log.Warn("Failed to extract image from {0} with {1} {2}", source.GetDebugName(), info.FileName, info.Arguments); return false; } return true; }
public static Stream ExtractImage(MediaSource source, int startPosition, int? maxWidth, int? maxHeight) { if (!source.Exists) { Log.Warn("ExtractImage: Source {0} (resolved to path {1}) doesn't exists", source.GetDebugName(), source.GetPath()); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound); return Stream.Null; } if (!source.SupportsDirectAccess) { Log.Warn("ExtractImage: Extracting images from remote sources isn't supported yet", source.MediaType, source.Id); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented); return Stream.Null; } // calculate size string ffmpegResize = ""; if (maxWidth != null || maxHeight != null) { try { decimal resolution = MediaInfoWrapper.GetMediaInfo(source).VideoStreams.First().DisplayAspectRatio; ffmpegResize = "-s " + Resolution.Calculate(resolution, maxWidth, maxHeight).ToString(); } catch (Exception ex) { Log.Error("Error while getting resolution of video stream, not resizing", ex); } } // get temporary filename string tempDir = Path.Combine(Installation.GetCacheDirectory(), "imagecache"); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } string filename = String.Format("extract_{0}_{1}_{2}_{3}.jpg", source.GetUniqueIdentifier(), startPosition, maxWidth == null ? "null" : maxWidth.ToString(), maxHeight == null ? "null" : maxHeight.ToString()); string tempFile = Path.Combine(tempDir, filename); // maybe it exists in cache, return that then if (File.Exists(tempFile)) { return StreamImage(new ImageMediaSource(tempFile)); } // execute it ProcessStartInfo info = new ProcessStartInfo(); using (var impersonator = source.GetImpersonator()) { info.Arguments = String.Format("-ss {0} -i \"{1}\" {2} -vframes 1 -f image2 {3}", startPosition, source.GetPath(), ffmpegResize, tempFile); info.FileName = Configuration.Streaming.FFMpegPath; info.CreateNoWindow = true; info.UseShellExecute = false; Process proc = new Process(); proc.StartInfo = info; proc.Start(); proc.WaitForExit(); } // log when failed if (!File.Exists(tempFile)) { Log.Warn("Failed to extract image to temporary file {0} with command {1}", tempFile, info.Arguments); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError); return Stream.Null; } return StreamImage(new ImageMediaSource(tempFile)); }
public static Stream ExtractImage(MediaSource source, int startPosition, int?maxWidth, int?maxHeight) { if (!source.Exists) { Log.Warn("ExtractImage: Source {0} (resolved to path {1}) doesn't exists", source.GetDebugName(), source.GetPath()); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound); return(Stream.Null); } if (!source.SupportsDirectAccess) { Log.Warn("ExtractImage: Extracting images from remote sources isn't supported yet", source.MediaType, source.Id); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented); return(Stream.Null); } // calculate size string ffmpegResize = ""; if (maxWidth != null || maxHeight != null) { try { decimal resolution = MediaInfoWrapper.GetMediaInfo(source).VideoStreams.First().DisplayAspectRatio; ffmpegResize = "-s " + Resolution.Calculate(resolution, maxWidth, maxHeight).ToString(); } catch (Exception ex) { Log.Error("Error while getting resolution of video stream, not resizing", ex); } } // get temporary filename string tempDir = Path.Combine(Path.GetTempPath(), "MPExtended", "imagecache"); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } string filename = String.Format("ex_{0}_{1}_{2}_{3}.jpg", source.GetUniqueIdentifier(), startPosition, maxWidth == null ? "null" : maxWidth.ToString(), maxHeight == null ? "null" : maxHeight.ToString()); string tempFile = Path.Combine(tempDir, filename); // maybe it exists in cache, return that then if (File.Exists(tempFile)) { return(StreamImage(new ImageMediaSource(tempFile))); } // execute it ProcessStartInfo info = new ProcessStartInfo(); using (var impersonator = source.GetImpersonator()) { info.Arguments = String.Format("-ss {0} -i \"{1}\" {2} -vframes 1 -f image2 {3}", startPosition, source.GetPath(), ffmpegResize, tempFile); info.FileName = Configuration.Streaming.FFMpegPath; info.CreateNoWindow = true; info.UseShellExecute = false; Process proc = new Process(); proc.StartInfo = info; proc.Start(); proc.WaitForExit(); } // log when failed if (!File.Exists(tempFile)) { Log.Warn("Failed to extract image to temporary file {0} with command {1}", tempFile, info.Arguments); WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError); return(Stream.Null); } return(StreamImage(new ImageMediaSource(tempFile))); }
public Stream GetMediaItem(WebStreamMediaType type, int? provider, string itemId) { MediaSource source = new MediaSource(type, provider, itemId); try { if (!source.Exists) { throw new FileNotFoundException(); } return source.Retrieve(); } catch (Exception ex) { WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound); Log.Info(String.Format("GetMediaItem() failed for {0}", source.GetDebugName()), ex); return Stream.Null; } }
public bool HasForSource(MediaSource src) { return cache.ContainsKey(src.GetUniqueIdentifier()); }
public void Save(MediaSource src, CachedInfoWrapper info) { lock (cache) cache[src.GetUniqueIdentifier()] = info; isDirty = true; }
public CachedInfoWrapper GetForSource(MediaSource src) { return cache[src.GetUniqueIdentifier()]; }
/// <param name="smartHash">Use smartHash for calculating a quick hash (only uses part of the media item) or a full MD5 hash</param> public WebMediaHash GetItemHash(WebMediaType type, int? provider, string itemId, int? offset, bool smartHash) { MediaSource source = new MediaSource(type, provider, itemId, offset); String error = source.CheckAvailability(); if (error != null) { return new WebMediaHash() { Generated = false, Error = error }; } return new WebMediaHash() { Generated = true, Hash = smartHash ? source.ComputeSmartHash() : source.ComputeFullHash() }; }
public WebItemSupportStatus GetItemSupportStatus(WebMediaType type, int? provider, string itemId, int? offset) { // check if we actually now about this file MediaSource source = new MediaSource(type, provider, itemId, offset); string error = source.CheckAvailability(); return error != null ? new WebItemSupportStatus(false, error) : new WebItemSupportStatus(true, null); }
public void Save(MediaSource src, WebMediaInfo info) { lock (cache) { cache[src.GetUniqueIdentifier()] = info; } isDirty = true; }
public void EndStream(MediaSource source, bool force = false) { // generate identifier string identifier = GetIdentifierFromMediaSource(source); // ignore if not registered or already stale if (!enabled || !streams.ContainsKey(identifier) || streams[identifier].Stale) { return; } if (streams[identifier].TranscodingInfo != null && streams[identifier].TranscodingInfo.Value != null) { int progress = CalculateWatchPosition(identifier); if (progress >= 95) { Log.Debug("WatchSharing: seeing {0}% as finished for {1}", progress, identifier); // send the finished event in the background thread Task.Factory.StartNew(delegate() { // Stop the timers. Do this before sending the FinishEpisode() events as we could get a race condition with the service otherwise. foreach (var timer in streams[identifier].BackgroundTimers) { timer.Enabled = false; } // Send the FinishEpisode event if (streams[identifier].Context.Source.MediaType == WebMediaType.TVEpisode) { services.ExecuteForAll(s => CallForEpisode(identifier, s.FinishEpisode)); } else if (streams[identifier].Context.Source.MediaType == WebMediaType.Movie) { services.ExecuteForAll(s => s.FinishMovie((WebMovieDetailed)streams[identifier].MediaDescriptor)); } // And definitely stop the stream streams.Remove(identifier); Log.Debug("WatchSharing: finished handling {0}", identifier); }).LogOnException(); return; } } // cancel it if (!force) { Log.Debug("WatchSharing: canceling stream {0}", identifier); streams[identifier].Canceled = true; } else { // definitely cancel it Log.Debug("WatchSharing: killing stream {0} because of forced EndStream", identifier); if (streams[identifier].Context.Source.MediaType == WebMediaType.TVEpisode) { services.ExecuteForAll(s => CallForEpisode(identifier, s.CancelWatchingEpisode)); } else if (streams[identifier].Context.Source.MediaType == WebMediaType.Movie) { services.ExecuteForAll(s => s.CancelWatchingMovie((WebMovieDetailed)streams[identifier].MediaDescriptor)); } } }
private string GetIdentifierFromMediaSource(MediaSource source) { return Enum.GetName(typeof(WebStreamMediaType), source.MediaType) + "_" + source.Id; }
public WebMediaInfo GetForSource(MediaSource src) { return cache[src.GetUniqueIdentifier()]; }
public bool InitStream(string identifier, string clientDescription, MediaSource source, int timeout) { if (!source.Exists) { StreamLog.Warn(identifier, "Tried to start stream for non-existing file {0}", source.GetDebugName()); return false; } ActiveStream stream = new ActiveStream(); stream.Identifier = identifier; stream.ClientDescription = clientDescription; stream.StartTime = DateTime.Now; stream.Timeout = timeout; stream.LastActivity = DateTime.Now; stream.UseActivityForTimeout = false; stream.Context = new StreamContext(); stream.Context.Identifier = identifier; stream.Context.Source = source; stream.Context.IsTv = source.MediaType == WebMediaType.TV; // Some clients such as WebMP proxy the streams before relying it to the client. We should give these clients the option to // forward the real IP address, so that we can show that one in the configurator too. However, to avoid abuse, we should show // the real IP of the client too, so make up some nice text string. string realIp = WCFUtil.GetHeaderValue("forwardedFor", "X-Forwarded-For"); stream.ClientIP = realIp == null ? WCFUtil.GetClientIPAddress() : String.Format("{0} (via {1})", realIp, WCFUtil.GetClientIPAddress()); Streams[identifier] = stream; return true; }
public Stream GetMediaItem(WebStreamMediaType type, int? provider, string itemId) { MediaSource source = new MediaSource(type, provider, itemId); return source.Retrieve(); }