コード例 #1
0
ファイル: BaseEncoder.cs プロジェクト: Ivan-L/Emby
        private string GetInputPathArgument(EncodingJob state)
        {
            var protocol  = state.InputProtocol;
            var mediaPath = state.MediaPath ?? string.Empty;

            var inputPath = new[] { mediaPath };

            if (state.IsInputVideo)
            {
                if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
                {
                    inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
                }
            }

            return(MediaEncoder.GetInputArgument(inputPath, protocol));
        }
コード例 #2
0
        /// <summary>
        /// Gets the input argument.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="isoMount">The iso mount.</param>
        /// <returns>System.String.</returns>
        protected string GetInputArgument(BaseItem item, IIsoMount isoMount)
        {
            var type = InputType.AudioFile;

            var inputPath = new[] { item.Path };

            var video = item as Video;

            if (video != null)
            {
                if (!(video.VideoType == VideoType.Iso && isoMount == null))
                {
                    inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type);
                }
            }

            return(MediaEncoder.GetInputArgument(inputPath, type));
        }
コード例 #3
0
        /// <summary>
        /// Gets the probe size argument.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>System.String.</returns>
        protected string GetProbeSizeArgument(BaseItem item)
        {
            var type = InputType.AudioFile;

            if (item is Audio)
            {
                type = MediaEncoderHelpers.GetInputType(item.Path, null, null);
            }
            else
            {
                var video = item as Video;

                if (video != null)
                {
                    type = MediaEncoderHelpers.GetInputType(item.Path, video.VideoType, video.IsoType);
                }
            }

            return(MediaEncoder.GetProbeSizeArgument(type));
        }
コード例 #4
0
        private async Task <InternalMediaInfoResult> GetMediaInfo(BaseItem item,
                                                                  IIsoMount isoMount,
                                                                  CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var idString  = item.Id.ToString("N");
            var cachePath = Path.Combine(_appPaths.CachePath,
                                         "ffprobe-video",
                                         idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");

            try
            {
                return(_json.DeserializeFromFile <InternalMediaInfoResult>(cachePath));
            }
            catch (FileNotFoundException)
            {
            }
            catch (DirectoryNotFoundException)
            {
            }

            var type      = InputType.File;
            var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath };

            var video = item as Video;

            if (video != null)
            {
                inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, out type);
            }

            var result = await _mediaEncoder.GetMediaInfo(inputPath, type, false, cancellationToken).ConfigureAwait(false);

            Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
            _json.SerializeToFile(result, cachePath);

            return(result);
        }
コード例 #5
0
        /// <summary>
        /// Fetches the specified audio.
        /// </summary>
        /// <param name="audio">The audio.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="data">The data.</param>
        /// <returns>Task.</returns>
        protected Task Fetch(Audio audio, CancellationToken cancellationToken, InternalMediaInfoResult data)
        {
            var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;

            audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.Video);

            if (data.streams != null)
            {
                // Get the first audio stream
                var stream = data.streams.FirstOrDefault(s => string.Equals(s.codec_type, "audio", StringComparison.OrdinalIgnoreCase));

                if (stream != null)
                {
                    // Get duration from stream properties
                    var duration = stream.duration;

                    // If it's not there go into format properties
                    if (string.IsNullOrEmpty(duration))
                    {
                        duration = data.format.duration;
                    }

                    // If we got something, parse it
                    if (!string.IsNullOrEmpty(duration))
                    {
                        audio.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks;
                    }
                }
            }

            if (data.format.tags != null)
            {
                FetchDataFromTags(audio, data.format.tags);
            }

            return(_itemRepo.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken));
        }
コード例 #6
0
        public async Task <DynamicImageResponse> GetVideoImage(Video item, CancellationToken cancellationToken)
        {
            var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false);

            try
            {
                // If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in.
                // Always use 10 seconds for dvd because our duration could be out of whack
                var imageOffset = item.VideoType != VideoType.Dvd && item.RunTimeTicks.HasValue &&
                                  item.RunTimeTicks.Value > 0
                                      ? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
                                      : TimeSpan.FromSeconds(10);

                var protocol = item.LocationType == LocationType.Remote
                    ? MediaProtocol.Http
                    : MediaProtocol.File;

                var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, protocol, isoMount, item.PlayableStreamFileNames);

                var stream = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);

                return(new DynamicImageResponse
                {
                    Format = ImageFormat.Jpg,
                    HasImage = true,
                    Stream = stream
                });
            }
            finally
            {
                if (isoMount != null)
                {
                    isoMount.Dispose();
                }
            }
        }
コード例 #7
0
        public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationToken cancellationToken)
        {
            var protocol = item.PathProtocol ?? MediaProtocol.File;

            var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, null, item.GetPlayableStreamFileNames(_mediaEncoder));

            var mediaStreams =
                item.GetMediaStreams();

            var imageStreams =
                mediaStreams
                    .Where(i => i.Type == MediaStreamType.EmbeddedImage)
                    .ToList();

            var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ??
                imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
                imageStreams.FirstOrDefault();

            string extractedImagePath;

            if (imageStream != null)
            {
                // Instead of using the raw stream index, we need to use nth video/embedded image stream
                var videoIndex = -1;
                foreach (var mediaStream in mediaStreams)
                {
                    if (mediaStream.Type == MediaStreamType.Video ||
                        mediaStream.Type == MediaStreamType.EmbeddedImage)
                    {
                        videoIndex++;
                    }
                    if (mediaStream == imageStream)
                    {
                        break;
                    }
                }

                extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, imageStream, videoIndex, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                // If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in.
                // Always use 10 seconds for dvd because our duration could be out of whack
                var imageOffset = item.VideoType != VideoType.Dvd && item.RunTimeTicks.HasValue &&
                                  item.RunTimeTicks.Value > 0
                                      ? TimeSpan.FromTicks(Convert.ToInt64(item.RunTimeTicks.Value * .1))
                                      : TimeSpan.FromSeconds(10);

                var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

                extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, videoStream, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
            }

            return new DynamicImageResponse
            {
                Format = ImageFormat.Jpg,
                HasImage = true,
                Path = extractedImagePath,
                Protocol = MediaProtocol.File
            };
        }
コード例 #8
0
        public async Task <bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List <ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
        {
            if (!IsEligibleForChapterImageExtraction(video))
            {
                extractImages = false;
            }

            var success     = true;
            var changesMade = false;

            var runtimeTicks = video.RunTimeTicks ?? 0;

            var currentImages = GetSavedChapterImages(video, directoryService);

            foreach (var chapter in chapters)
            {
                if (chapter.StartPositionTicks >= runtimeTicks)
                {
                    _logger.LogInformation("Stopping chapter extraction for {0} because a chapter was found with a position greater than the runtime.", video.Name);
                    break;
                }

                var path = GetChapterImagePath(video, chapter.StartPositionTicks);

                if (!currentImages.Contains(path, StringComparer.OrdinalIgnoreCase))
                {
                    if (extractImages)
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        try
                        {
                            // Add some time for the first chapter to make sure we don't end up with a black image
                            var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);

                            var protocol = MediaProtocol.File;

                            var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, Array.Empty <string>());

                            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));

                            var container = video.Container;

                            var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);

                            _fileSystem.CopyFile(tempFile, path, true);

                            try
                            {
                                _fileSystem.DeleteFile(tempFile);
                            }
                            catch
                            {
                            }

                            chapter.ImagePath         = path;
                            chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
                            changesMade = true;
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Error extracting chapter images for {0}", string.Join(",", video.Path));
                            success = false;
                            break;
                        }
                    }
                    else if (!string.IsNullOrEmpty(chapter.ImagePath))
                    {
                        chapter.ImagePath = null;
                        changesMade       = true;
                    }
                }
                else if (!string.Equals(path, chapter.ImagePath, StringComparison.OrdinalIgnoreCase))
                {
                    chapter.ImagePath         = path;
                    chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
                    changesMade = true;
                }
            }

            if (saveChapters && changesMade)
            {
                _chapterManager.SaveChapters(video.Id.ToString(), chapters);
            }

            DeleteDeadImages(currentImages, chapters);

            return(success);
        }
コード例 #9
0
        protected async Task Fetch(Video video,
                                   CancellationToken cancellationToken,
                                   InternalMediaInfoResult data,
                                   IIsoMount isoMount,
                                   BlurayDiscInfo blurayInfo,
                                   MetadataRefreshOptions options)
        {
            var mediaInfo    = MediaEncoderHelpers.GetMediaInfo(data);
            var mediaStreams = mediaInfo.MediaStreams;

            video.TotalBitrate = mediaInfo.TotalBitrate;
            video.FormatName   = (mediaInfo.Format ?? string.Empty)
                                 .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);

            if (data.format != null)
            {
                // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
                var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;

                if (needToSetRuntime && !string.IsNullOrEmpty(data.format.duration))
                {
                    video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
                }

                if (video.VideoType == VideoType.VideoFile)
                {
                    var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');

                    video.Container = extension;
                }
                else
                {
                    video.Container = null;
                }

                if (!string.IsNullOrEmpty(data.format.size))
                {
                    video.Size = long.Parse(data.format.size, _usCulture);
                }
                else
                {
                    video.Size = null;
                }
            }

            var mediaChapters = (data.Chapters ?? new MediaChapter[] { }).ToList();
            var chapters      = mediaChapters.Select(GetChapterInfo).ToList();

            if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay))
            {
                FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
            }

            await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);

            FetchWtvInfo(video, data);

            video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270);

            var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);

            video.VideoBitRate            = videoStream == null ? null : videoStream.BitRate;
            video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;

            video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);

            ExtractTimestamp(video);
            UpdateFromMediaInfo(video, videoStream);

            await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false);

            if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
                options.MetadataRefreshMode == MetadataRefreshMode.Default)
            {
                var chapterOptions = _chapterManager.GetConfiguration();

                try
                {
                    var remoteChapters = await DownloadChapters(video, chapters, chapterOptions, cancellationToken).ConfigureAwait(false);

                    if (remoteChapters.Count > 0)
                    {
                        chapters = remoteChapters;
                    }
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error downloading chapters", ex);
                }

                if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
                {
                    AddDummyChapters(video, chapters);
                }

                NormalizeChapterNames(chapters);

                await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions
                {
                    Chapters      = chapters,
                    Video         = video,
                    ExtractImages = chapterOptions.ExtractDuringLibraryScan,
                    SaveChapters  = false
                }, cancellationToken).ConfigureAwait(false);

                await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
            }
        }
コード例 #10
0
 /// <summary>
 /// Gets the probe size argument.
 /// </summary>
 /// <param name="item">The item.</param>
 /// <returns>System.String.</returns>
 protected string GetProbeSizeArgument(BaseItem item)
 {
     return(MediaEncoder.GetProbeSizeArgument(MediaEncoderHelpers.GetInputType(item)));
 }
コード例 #11
0
        public async Task <bool> RefreshChapterImages(ChapterImageRefreshOptions options, CancellationToken cancellationToken)
        {
            var extractImages = options.ExtractImages;
            var video         = options.Video;
            var chapters      = options.Chapters;
            var saveChapters  = options.SaveChapters;

            if (!IsEligibleForChapterImageExtraction(video))
            {
                extractImages = false;
            }

            var success     = true;
            var changesMade = false;

            var runtimeTicks = video.RunTimeTicks ?? 0;

            var currentImages = GetSavedChapterImages(video);

            foreach (var chapter in chapters)
            {
                if (chapter.StartPositionTicks >= runtimeTicks)
                {
                    _logger.Info("Stopping chapter extraction for {0} because a chapter was found with a position greater than the runtime.", video.Name);
                    break;
                }

                var path = GetChapterImagePath(video, chapter.StartPositionTicks);

                if (!currentImages.Contains(path, StringComparer.OrdinalIgnoreCase))
                {
                    if (extractImages)
                    {
                        if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay)
                        {
                            continue;
                        }

                        // Add some time for the first chapter to make sure we don't end up with a black image
                        var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);

                        var protocol = MediaProtocol.File;

                        var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames);

                        try
                        {
                            _fileSystem.CreateDirectory(Path.GetDirectoryName(path));

                            using (var stream = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false))
                            {
                                using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
                                {
                                    await stream.CopyToAsync(fileStream).ConfigureAwait(false);
                                }
                            }

                            chapter.ImagePath = path;
                            changesMade       = true;
                        }
                        catch (Exception ex)
                        {
                            _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", inputPath));
                            success = false;
                            break;
                        }
                    }
                    else if (!string.IsNullOrEmpty(chapter.ImagePath))
                    {
                        chapter.ImagePath = null;
                        changesMade       = true;
                    }
                }
                else if (!string.Equals(path, chapter.ImagePath, StringComparison.OrdinalIgnoreCase))
                {
                    chapter.ImagePath = path;
                    changesMade       = true;
                }
            }

            if (saveChapters && changesMade)
            {
                await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
            }

            DeleteDeadImages(currentImages, chapters);

            return(success);
        }
コード例 #12
0
        private async Task <string> GetBifFile(GetBifFile request)
        {
            var widthVal = request.MaxWidth.HasValue ? request.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty;

            var item         = _libraryManager.GetItemById(request.Id);
            var mediaSources = ((IHasMediaSources)item).GetMediaSources(false).ToList();
            var mediaSource  = mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)) ?? mediaSources.First();

            var path = Path.Combine(_appPaths.ImageCachePath, "bif", request.Id, request.MediaSourceId, widthVal, "index.bif");

            if (File.Exists(path))
            {
                return(path);
            }

            var protocol = mediaSource.Protocol;

            var inputPath = MediaEncoderHelpers.GetInputArgument(mediaSource.Path, protocol, null, mediaSource.PlayableStreamFileNames);

            var semaphore = GetLock(path);

            await semaphore.WaitAsync().ConfigureAwait(false);

            try
            {
                if (File.Exists(path))
                {
                    return(path);
                }

                await _mediaEncoder.ExtractVideoImagesOnInterval(inputPath, protocol, mediaSource.Video3DFormat,
                                                                 TimeSpan.FromSeconds(10), Path.GetDirectoryName(path), "img_", request.MaxWidth, CancellationToken.None)
                .ConfigureAwait(false);

                var images = new DirectoryInfo(Path.GetDirectoryName(path))
                             .EnumerateFiles()
                             .Where(img => string.Equals(img.Extension, ".jpg", StringComparison.Ordinal))
                             .OrderBy(i => i.FullName)
                             .ToList();

                using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
                {
                    var magicNumber = new byte[] { 0x89, 0x42, 0x49, 0x46, 0x0d, 0x0a, 0x1a, 0x0a };
                    await fs.WriteAsync(magicNumber, 0, magicNumber.Length);

                    // version
                    var bytes = GetBytes(0);
                    await fs.WriteAsync(bytes, 0, bytes.Length);

                    // image count
                    bytes = GetBytes(images.Count);
                    await fs.WriteAsync(bytes, 0, bytes.Length);

                    // interval in ms
                    bytes = GetBytes(10000);
                    await fs.WriteAsync(bytes, 0, bytes.Length);

                    // reserved
                    for (var i = 20; i <= 63; i++)
                    {
                        bytes = new byte[] { 0x00 };
                        await fs.WriteAsync(bytes, 0, bytes.Length);
                    }

                    // write the bif index
                    var  index       = 0;
                    long imageOffset = 64 + (8 * images.Count) + 8;

                    foreach (var img in images)
                    {
                        bytes = GetBytes(index);
                        await fs.WriteAsync(bytes, 0, bytes.Length);

                        bytes = GetBytes(imageOffset);
                        await fs.WriteAsync(bytes, 0, bytes.Length);

                        imageOffset += img.Length;

                        index++;
                    }

                    bytes = new byte[] { 0xff, 0xff, 0xff, 0xff };
                    await fs.WriteAsync(bytes, 0, bytes.Length);

                    bytes = GetBytes(imageOffset);
                    await fs.WriteAsync(bytes, 0, bytes.Length);

                    // write the images
                    foreach (var img in images)
                    {
                        using (var imgStream = _fileSystem.GetFileStream(img.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
                        {
                            await imgStream.CopyToAsync(fs).ConfigureAwait(false);
                        }
                    }
                }

                return(path);
            }
            finally
            {
                semaphore.Release();
            }
        }