示例#1
0
        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();
            }
        }
示例#2
0
        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));
        }
示例#3
0
        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);
        }
示例#4
0
        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;
        }
示例#5
0
        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;
        }
示例#6
0
        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);
        }
示例#7
0
        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;
        }
示例#8
0
        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;
        }
示例#9
0
        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;
            }
        }
示例#10
0
        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);
        }
示例#11
0
        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);
        }
示例#12
0
        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;
        }
示例#13
0
        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);
                    }
                }
            }
        }
示例#14
0
        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;
        }
示例#15
0
        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 };
        }
示例#16
0
 public static Stream ExtractImage(MediaSource source, long position, string format)
 {
     return Images.ExtractImage(source, position, null, null, null, format);
 }
示例#17
0
        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);
        }
示例#18
0
 private string GetIdentifierFromMediaSource(MediaSource source)
 {
     return(Enum.GetName(typeof(WebMediaType), source.MediaType) + "_" + source.Id);
 }
示例#19
0
        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;
        }
示例#20
0
        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));
        }
示例#21
0
        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)));
        }
示例#22
0
        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);
                    }
                }
            }
        }
示例#23
0
 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;
     }
 }
示例#24
0
 public bool HasForSource(MediaSource src)
 {
     return cache.ContainsKey(src.GetUniqueIdentifier());
 }
示例#25
0
        public void Save(MediaSource src, CachedInfoWrapper info)
        {
            lock (cache)
                cache[src.GetUniqueIdentifier()] = info;

            isDirty = true;
        }
示例#26
0
 public CachedInfoWrapper GetForSource(MediaSource src)
 {
     return cache[src.GetUniqueIdentifier()];
 }
示例#27
0
        /// <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() };
        }
示例#28
0
 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);
 }
示例#29
0
 public void Save(MediaSource src, WebMediaInfo info)
 {
     lock (cache)
     {
         cache[src.GetUniqueIdentifier()] = info;
     }
     isDirty = true;
 }
示例#30
0
        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));
                }
            }
        }
示例#31
0
 private string GetIdentifierFromMediaSource(MediaSource source)
 {
     return Enum.GetName(typeof(WebStreamMediaType), source.MediaType) + "_" + source.Id;
 }
示例#32
0
 public WebMediaInfo GetForSource(MediaSource src)
 {
     return cache[src.GetUniqueIdentifier()];
 }
示例#33
0
        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;
        }
示例#34
0
 public Stream GetMediaItem(WebStreamMediaType type, int? provider, string itemId)
 {
     MediaSource source = new MediaSource(type, provider, itemId);
     return source.Retrieve();
 }