Esempio n. 1
0
File: Video.cs Progetto: leaty/Emby
        private static MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, Video media, MediaSourceType type)
        {
            if (media == null)
            {
                throw new ArgumentNullException("media");
            }

            var mediaStreams = MediaSourceManager.GetMediaStreams(media.Id);

            var locationType = media.LocationType;

            var info = new MediaSourceInfo
            {
                Id                   = media.Id.ToString("N"),
                IsoType              = media.IsoType,
                Protocol             = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
                MediaStreams         = mediaStreams,
                Name                 = GetMediaSourceName(media, mediaStreams),
                Path                 = enablePathSubstitution ? GetMappedPath(media, media.Path, locationType) : media.Path,
                RunTimeTicks         = media.RunTimeTicks,
                Video3DFormat        = media.Video3DFormat,
                VideoType            = media.VideoType,
                Container            = media.Container,
                Size                 = media.Size,
                Timestamp            = media.Timestamp,
                Type                 = type,
                SupportsDirectStream = media.VideoType == VideoType.VideoFile,
                IsRemote             = media.IsShortcut
            };

            if (info.Protocol == MediaProtocol.File)
            {
                info.ETag = media.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N");
            }

            if (media.IsShortcut)
            {
                info.Path = media.ShortcutPath;

                if (!string.IsNullOrWhiteSpace(info.Path))
                {
                    if (info.Path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
                    {
                        info.Protocol             = MediaProtocol.Http;
                        info.SupportsDirectStream = false;
                    }
                    else if (info.Path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
                    {
                        info.Protocol             = MediaProtocol.Rtmp;
                        info.SupportsDirectStream = false;
                    }
                    else if (info.Path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
                    {
                        info.Protocol             = MediaProtocol.Rtsp;
                        info.SupportsDirectStream = false;
                    }
                    else
                    {
                        info.Protocol = MediaProtocol.File;
                    }
                }
            }

            if (string.IsNullOrEmpty(info.Container))
            {
                if (media.VideoType == VideoType.VideoFile || media.VideoType == VideoType.Iso)
                {
                    if (!string.IsNullOrWhiteSpace(media.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
                    {
                        info.Container = System.IO.Path.GetExtension(media.Path).TrimStart('.');
                    }
                }
            }

            info.Bitrate = media.TotalBitrate;
            info.InferTotalBitrate();

            return(info);
        }
Esempio n. 2
0
 public Task <string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int?imageStreamIndex, CancellationToken cancellationToken)
 {
     return(ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, cancellationToken));
 }
Esempio n. 3
0
        public async Task ExtractVideoImagesOnInterval(
            string inputFile,
            string container,
            MediaStream videoStream,
            MediaSourceInfo mediaSource,
            Video3DFormat?threedFormat,
            TimeSpan interval,
            string targetDirectory,
            string filenamePrefix,
            int?maxWidth,
            CancellationToken cancellationToken)
        {
            var inputArgument = GetInputArgument(inputFile, mediaSource);

            var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture);

            if (maxWidth.HasValue)
            {
                var maxWidthParam = maxWidth.Value.ToString(_usCulture);

                vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
            }

            Directory.CreateDirectory(targetDirectory);
            var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");

            var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads);

            var probeSizeArgument       = string.Empty;
            var analyzeDurationArgument = string.Empty;

            if (!string.IsNullOrWhiteSpace(probeSizeArgument))
            {
                args = probeSizeArgument + " " + args;
            }

            if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
            {
                args = analyzeDurationArgument + " " + args;
            }

            if (videoStream != null)
            {
                /* fix
                 * var decoder = encodinghelper.GetHardwareAcceleratedVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions());
                 * if (!string.IsNullOrWhiteSpace(decoder))
                 * {
                 *  args = decoder + " " + args;
                 * }
                 */
            }

            if (!string.IsNullOrWhiteSpace(container))
            {
                var inputFormat = EncodingHelper.GetInputFormat(container);
                if (!string.IsNullOrWhiteSpace(inputFormat))
                {
                    args = "-f " + inputFormat + " " + args;
                }
            }

            var processStartInfo = new ProcessStartInfo
            {
                CreateNoWindow  = true,
                UseShellExecute = false,
                FileName        = _ffmpegPath,
                Arguments       = args,
                WindowStyle     = ProcessWindowStyle.Hidden,
                ErrorDialog     = false
            };

            _logger.LogInformation(processStartInfo.FileName + " " + processStartInfo.Arguments);

            await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);

            bool ranToCompletion = false;

            var process = new Process
            {
                StartInfo           = processStartInfo,
                EnableRaisingEvents = true
            };

            using (var processWrapper = new ProcessWrapper(process, this))
            {
                try
                {
                    StartProcess(processWrapper);

                    // Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
                    // but we still need to detect if the process hangs.
                    // Making the assumption that as long as new jpegs are showing up, everything is good.

                    bool isResponsive = true;
                    int  lastCount    = 0;

                    while (isResponsive)
                    {
                        if (await process.WaitForExitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false))
                        {
                            ranToCompletion = true;
                            break;
                        }

                        cancellationToken.ThrowIfCancellationRequested();

                        var jpegCount = _fileSystem.GetFilePaths(targetDirectory)
                                        .Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));

                        isResponsive = jpegCount > lastCount;
                        lastCount    = jpegCount;
                    }

                    if (!ranToCompletion)
                    {
                        StopProcess(processWrapper, 1000);
                    }
                }
                finally
                {
                    _thumbnailResourcePool.Release();
                }

                var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;

                if (exitCode == -1)
                {
                    var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument);

                    _logger.LogError(msg);

                    throw new Exception(msg);
                }
            }
        }
Esempio n. 4
0
 public Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, bool addProbeDelay, CancellationToken cancellationToken)
 {
     return(AddMediaInfoWithProbe(mediaSource, isAudio, null, addProbeDelay, cancellationToken));
 }
Esempio n. 5
0
        internal static void AttachMediaSourceInfo(EncodingJob state,
                                                   MediaSourceInfo mediaSource,
                                                   EncodingJobOptions videoRequest)
        {
            state.MediaPath         = mediaSource.Path;
            state.InputProtocol     = mediaSource.Protocol;
            state.InputContainer    = mediaSource.Container;
            state.InputFileSize     = mediaSource.Size;
            state.InputBitrate      = mediaSource.Bitrate;
            state.RunTimeTicks      = mediaSource.RunTimeTicks;
            state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;

            if (mediaSource.VideoType.HasValue)
            {
                state.VideoType = mediaSource.VideoType.Value;
            }

            state.IsoType = mediaSource.IsoType;

            state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();

            if (mediaSource.Timestamp.HasValue)
            {
                state.InputTimestamp = mediaSource.Timestamp.Value;
            }

            state.InputProtocol              = mediaSource.Protocol;
            state.MediaPath                  = mediaSource.Path;
            state.RunTimeTicks               = mediaSource.RunTimeTicks;
            state.RemoteHttpHeaders          = mediaSource.RequiredHttpHeaders;
            state.InputBitrate               = mediaSource.Bitrate;
            state.InputFileSize              = mediaSource.Size;
            state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;

            if (state.ReadInputAtNativeFramerate ||
                mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
            {
                state.OutputAudioSync = "1000";
                state.InputVideoSync  = "-1";
                state.InputAudioSync  = "1";
            }

            if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase))
            {
                // Seeing some stuttering when transcoding wma to audio-only HLS
                state.InputAudioSync = "1";
            }

            var mediaStreams = mediaSource.MediaStreams;

            if (videoRequest != null)
            {
                if (string.IsNullOrEmpty(videoRequest.VideoCodec))
                {
                    videoRequest.VideoCodec = InferVideoCodec(videoRequest.OutputContainer);
                }

                state.VideoStream    = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video);
                state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
                state.AudioStream    = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);

                if (state.SubtitleStream != null && !state.SubtitleStream.IsExternal)
                {
                    state.InternalSubtitleStreamOffset = mediaStreams.Where(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal).ToList().IndexOf(state.SubtitleStream);
                }

                if (state.VideoStream != null && state.VideoStream.IsInterlaced)
                {
                    state.DeInterlace = true;
                }

                EnforceResolutionLimit(state, videoRequest);
            }
            else
            {
                state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
            }

            state.MediaSource = mediaSource;
        }
Esempio n. 6
0
        private void SetDeviceSpecificData(BaseItem item,
                                           MediaSourceInfo mediaSource,
                                           DeviceProfile profile,
                                           AuthorizationInfo auth,
                                           int?maxBitrate,
                                           long startTimeTicks,
                                           string mediaSourceId,
                                           int?audioStreamIndex,
                                           int?subtitleStreamIndex,
                                           string playSessionId)
        {
            var streamBuilder = new StreamBuilder(Logger);

            var options = new VideoOptions
            {
                MediaSources = new List <MediaSourceInfo> {
                    mediaSource
                },
                Context  = EncodingContext.Streaming,
                DeviceId = auth.DeviceId,
                ItemId   = item.Id.ToString("N"),
                Profile  = profile
            };

            if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
            {
                options.MediaSourceId       = mediaSourceId;
                options.AudioStreamIndex    = audioStreamIndex;
                options.SubtitleStreamIndex = subtitleStreamIndex;
            }

            if (mediaSource.SupportsDirectPlay)
            {
                var supportsDirectStream = mediaSource.SupportsDirectStream;

                // Dummy this up to fool StreamBuilder
                mediaSource.SupportsDirectStream = true;
                options.MaxBitrate = maxBitrate;

                // The MediaSource supports direct stream, now test to see if the client supports it
                var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
                                 streamBuilder.BuildAudioItem(options) :
                                 streamBuilder.BuildVideoItem(options);

                if (streamInfo == null || !streamInfo.IsDirectStream)
                {
                    mediaSource.SupportsDirectPlay = false;
                }

                // Set this back to what it was
                mediaSource.SupportsDirectStream = supportsDirectStream;

                if (streamInfo != null)
                {
                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                }
            }

            if (mediaSource.SupportsDirectStream)
            {
                options.MaxBitrate = GetMaxBitrate(maxBitrate);

                // The MediaSource supports direct stream, now test to see if the client supports it
                var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
                                 streamBuilder.BuildAudioItem(options) :
                                 streamBuilder.BuildVideoItem(options);

                if (streamInfo == null || !streamInfo.IsDirectStream)
                {
                    mediaSource.SupportsDirectStream = false;
                }

                if (streamInfo != null)
                {
                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                }
            }

            if (mediaSource.SupportsTranscoding)
            {
                options.MaxBitrate = GetMaxBitrate(maxBitrate);

                // The MediaSource supports direct stream, now test to see if the client supports it
                var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
                                 streamBuilder.BuildAudioItem(options) :
                                 streamBuilder.BuildVideoItem(options);

                if (streamInfo != null)
                {
                    streamInfo.PlaySessionId = playSessionId;
                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                }

                if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode)
                {
                    streamInfo.StartPositionTicks      = startTimeTicks;
                    mediaSource.TranscodingUrl         = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
                    mediaSource.TranscodingContainer   = streamInfo.Container;
                    mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
                }
            }
        }
Esempio n. 7
0
 private async Task <string> DownloadFile(SyncJobItem jobItem, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
 {
     // TODO: Download
     return(mediaSource.Path);
 }
Esempio n. 8
0
        public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
        {
            var originalRuntime = mediaSource.RunTimeTicks;

            var now = DateTime.UtcNow;

            var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
            {
                InputPath               = mediaSource.Path,
                Protocol                = mediaSource.Protocol,
                MediaType               = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
                ExtractChapters         = false,
                AnalyzeDurationSections = 2
            }, cancellationToken).ConfigureAwait(false);

            _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));

            mediaSource.Bitrate       = info.Bitrate;
            mediaSource.Container     = info.Container;
            mediaSource.Formats       = info.Formats;
            mediaSource.MediaStreams  = info.MediaStreams;
            mediaSource.RunTimeTicks  = info.RunTimeTicks;
            mediaSource.Size          = info.Size;
            mediaSource.Timestamp     = info.Timestamp;
            mediaSource.Video3DFormat = info.Video3DFormat;
            mediaSource.VideoType     = info.VideoType;

            mediaSource.DefaultSubtitleStreamIndex = null;

            // Null this out so that it will be treated like a live stream
            if (!originalRuntime.HasValue)
            {
                mediaSource.RunTimeTicks = null;
            }

            var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);

            if (audioStream == null || audioStream.Index == -1)
            {
                mediaSource.DefaultAudioStreamIndex = null;
            }
            else
            {
                mediaSource.DefaultAudioStreamIndex = audioStream.Index;
            }

            var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);

            if (videoStream != null)
            {
                if (!videoStream.BitRate.HasValue)
                {
                    var width = videoStream.Width ?? 1920;

                    if (width >= 1900)
                    {
                        videoStream.BitRate = 8000000;
                    }

                    else if (width >= 1260)
                    {
                        videoStream.BitRate = 3000000;
                    }

                    else if (width >= 700)
                    {
                        videoStream.BitRate = 1000000;
                    }
                }

                // This is coming up false and preventing stream copy
                videoStream.IsAVC = null;
            }

            // Try to estimate this
            mediaSource.InferTotalBitrate(true);
        }
Esempio n. 9
0
 private void NormalizeMediaSourceContainer(MediaSourceInfo mediaSource, DeviceProfile profile, DlnaProfileType type)
 {
     mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, mediaSource.Path, profile, type);
 }
Esempio n. 10
0
        private MediaSourceInfo GetLegacyMediaSource(TunerHostInfo info, string channelId, ChannelInfo channel)
        {
            int?   width        = null;
            int?   height       = null;
            bool   isInterlaced = true;
            string videoCodec   = null;
            string audioCodec   = null;

            int?videoBitrate = null;
            int?audioBitrate = null;

            if (channel != null)
            {
                if (string.IsNullOrWhiteSpace(videoCodec))
                {
                    videoCodec = channel.VideoCodec;
                }
                audioCodec = channel.AudioCodec;
            }

            // normalize
            if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase))
            {
                videoCodec = "mpeg2video";
            }

            string nal = null;

            var url = GetApiUrl(info, false);
            var id  = channelId;

            id += "_" + url.GetMD5().ToString("N");

            var mediaSource = new MediaSourceInfo
            {
                Path         = url,
                Protocol     = MediaProtocol.Udp,
                MediaStreams = new List <MediaStream>
                {
                    new MediaStream
                    {
                        Type = MediaStreamType.Video,
                        // Set the index to -1 because we don't know the exact index of the video stream within the container
                        Index         = -1,
                        IsInterlaced  = isInterlaced,
                        Codec         = videoCodec,
                        Width         = width,
                        Height        = height,
                        BitRate       = videoBitrate,
                        NalLengthSize = nal
                    },
                    new MediaStream
                    {
                        Type = MediaStreamType.Audio,
                        // Set the index to -1 because we don't know the exact index of the audio stream within the container
                        Index   = -1,
                        Codec   = audioCodec,
                        BitRate = audioBitrate
                    }
                },
                RequiresOpening = true,
                RequiresClosing = true,
                BufferMs        = 0,
                Container       = "ts",
                Id = id,
                SupportsDirectPlay   = false,
                SupportsDirectStream = true,
                SupportsTranscoding  = true,
                IsInfiniteStream     = true
            };

            mediaSource.InferTotalBitrate();

            return(mediaSource);
        }
Esempio n. 11
0
 public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
 {
     return(targetFile);
 }
Esempio n. 12
0
        private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, ChannelInfo channelInfo, string profile)
        {
            int?   width        = null;
            int?   height       = null;
            bool   isInterlaced = true;
            string videoCodec   = null;
            string audioCodec   = null;

            int?videoBitrate = null;
            int?audioBitrate = null;

            if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase))
            {
                width        = 1280;
                height       = 720;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 2000000;
            }
            else if (string.Equals(profile, "heavy", StringComparison.OrdinalIgnoreCase))
            {
                width        = 1920;
                height       = 1080;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 15000000;
            }
            else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase))
            {
                width        = 960;
                height       = 546;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 2500000;
            }
            else if (string.Equals(profile, "internet480", StringComparison.OrdinalIgnoreCase))
            {
                width        = 848;
                height       = 480;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 2000000;
            }
            else if (string.Equals(profile, "internet360", StringComparison.OrdinalIgnoreCase))
            {
                width        = 640;
                height       = 360;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 1500000;
            }
            else if (string.Equals(profile, "internet240", StringComparison.OrdinalIgnoreCase))
            {
                width        = 432;
                height       = 240;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 1000000;
            }

            if (channelInfo != null)
            {
                if (string.IsNullOrWhiteSpace(videoCodec))
                {
                    videoCodec = channelInfo.VideoCodec;
                }
                audioCodec = channelInfo.AudioCodec;

                if (!videoBitrate.HasValue)
                {
                    videoBitrate = (channelInfo.IsHD ?? true) ? 15000000 : 2000000;
                }
                audioBitrate = (channelInfo.IsHD ?? true) ? 448000 : 192000;
            }

            // normalize
            if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase))
            {
                videoCodec = "mpeg2video";
            }

            string nal = null;

            if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
            {
                nal = "0";
            }

            var url = GetApiUrl(info, true) + "/auto/v" + channelId;

            // If raw was used, the tuner doesn't support params
            if (!string.IsNullOrWhiteSpace(profile) &&
                !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
            {
                url += "?transcode=" + profile;
            }

            var id = profile;

            if (string.IsNullOrWhiteSpace(id))
            {
                id = "native";
            }
            id += "_" + url.GetMD5().ToString("N");

            var mediaSource = new MediaSourceInfo
            {
                Path         = url,
                Protocol     = MediaProtocol.Http,
                MediaStreams = new List <MediaStream>
                {
                    new MediaStream
                    {
                        Type = MediaStreamType.Video,
                        // Set the index to -1 because we don't know the exact index of the video stream within the container
                        Index         = -1,
                        IsInterlaced  = isInterlaced,
                        Codec         = videoCodec,
                        Width         = width,
                        Height        = height,
                        BitRate       = videoBitrate,
                        NalLengthSize = nal
                    },
                    new MediaStream
                    {
                        Type = MediaStreamType.Audio,
                        // Set the index to -1 because we don't know the exact index of the audio stream within the container
                        Index   = -1,
                        Codec   = audioCodec,
                        BitRate = audioBitrate
                    }
                },
                RequiresOpening = true,
                RequiresClosing = false,
                BufferMs        = 0,
                Container       = "ts",
                Id = id,
                SupportsDirectPlay   = false,
                SupportsDirectStream = true,
                SupportsTranscoding  = true,
                IsInfiniteStream     = true
            };

            mediaSource.InferTotalBitrate();

            return(mediaSource);
        }
Esempio n. 13
0
 public PlaybackInfoResponse()
 {
     MediaSources = new MediaSourceInfo[] { };
 }
Esempio n. 14
0
        private static MediaSourceInfo NormalizeMediaSource(BaseItem item, MediaSourceInfo info)
        {
            info.RunTimeTicks ??= item.RunTimeTicks;

            return(info);
        }
Esempio n. 15
0
        private bool EncodeVideo(MediaSourceInfo mediaSource)
        {
            var mediaStreams = mediaSource.MediaStreams ?? new List <MediaStream>();

            return(!mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced));
        }
Esempio n. 16
0
        private void SetDeviceSpecificData(
            BaseItem item,
            MediaSourceInfo mediaSource,
            DeviceProfile profile,
            AuthorizationInfo auth,
            long?maxBitrate,
            long startTimeTicks,
            string mediaSourceId,
            int?audioStreamIndex,
            int?subtitleStreamIndex,
            int?maxAudioChannels,
            string playSessionId,
            Guid userId,
            bool enableDirectPlay,
            bool forceDirectPlayRemoteMediaSource,
            bool enableDirectStream,
            bool enableTranscoding,
            bool allowVideoStreamCopy,
            bool allowAudioStreamCopy)
        {
            var streamBuilder = new StreamBuilder(_mediaEncoder, Logger);

            var options = new VideoOptions
            {
                MediaSources     = new[] { mediaSource },
                Context          = EncodingContext.Streaming,
                DeviceId         = auth.DeviceId,
                ItemId           = item.Id,
                Profile          = profile,
                MaxAudioChannels = maxAudioChannels
            };

            if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
            {
                options.MediaSourceId       = mediaSourceId;
                options.AudioStreamIndex    = audioStreamIndex;
                options.SubtitleStreamIndex = subtitleStreamIndex;
            }

            var user = _userManager.GetUserById(userId);

            if (!enableDirectPlay)
            {
                mediaSource.SupportsDirectPlay = false;
            }

            if (!enableDirectStream)
            {
                mediaSource.SupportsDirectStream = false;
            }

            if (!enableTranscoding)
            {
                mediaSource.SupportsTranscoding = false;
            }

            if (item is Audio)
            {
                Logger.LogInformation("User policy for {0}. EnableAudioPlaybackTranscoding: {1}", user.Name, user.Policy.EnableAudioPlaybackTranscoding);
            }
            else
            {
                Logger.LogInformation("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}",
                                      user.Name,
                                      user.Policy.EnablePlaybackRemuxing,
                                      user.Policy.EnableVideoPlaybackTranscoding,
                                      user.Policy.EnableAudioPlaybackTranscoding);
            }

            // Beginning of Playback Determination: Attempt DirectPlay first
            if (mediaSource.SupportsDirectPlay)
            {
                if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
                {
                    mediaSource.SupportsDirectPlay = false;
                }
                else
                {
                    var supportsDirectStream = mediaSource.SupportsDirectStream;

                    // Dummy this up to fool StreamBuilder
                    mediaSource.SupportsDirectStream = true;
                    options.MaxBitrate = maxBitrate;

                    if (item is Audio)
                    {
                        if (!user.Policy.EnableAudioPlaybackTranscoding)
                        {
                            options.ForceDirectPlay = true;
                        }
                    }
                    else if (item is Video)
                    {
                        if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
                        {
                            options.ForceDirectPlay = true;
                        }
                    }

                    // The MediaSource supports direct stream, now test to see if the client supports it
                    var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
                        ? streamBuilder.BuildAudioItem(options)
                        : streamBuilder.BuildVideoItem(options);

                    if (streamInfo == null || !streamInfo.IsDirectStream)
                    {
                        mediaSource.SupportsDirectPlay = false;
                    }

                    // Set this back to what it was
                    mediaSource.SupportsDirectStream = supportsDirectStream;

                    if (streamInfo != null)
                    {
                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                    }
                }
            }

            if (mediaSource.SupportsDirectStream)
            {
                if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
                {
                    mediaSource.SupportsDirectStream = false;
                }
                else
                {
                    options.MaxBitrate = GetMaxBitrate(maxBitrate, user);

                    if (item is Audio)
                    {
                        if (!user.Policy.EnableAudioPlaybackTranscoding)
                        {
                            options.ForceDirectStream = true;
                        }
                    }
                    else if (item is Video)
                    {
                        if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
                        {
                            options.ForceDirectStream = true;
                        }
                    }

                    // The MediaSource supports direct stream, now test to see if the client supports it
                    var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
                        ? streamBuilder.BuildAudioItem(options)
                        : streamBuilder.BuildVideoItem(options);

                    if (streamInfo == null || !streamInfo.IsDirectStream)
                    {
                        mediaSource.SupportsDirectStream = false;
                    }

                    if (streamInfo != null)
                    {
                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                    }
                }
            }

            if (mediaSource.SupportsTranscoding)
            {
                options.MaxBitrate = GetMaxBitrate(maxBitrate, user);

                // The MediaSource supports direct stream, now test to see if the client supports it
                var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
                    ? streamBuilder.BuildAudioItem(options)
                    : streamBuilder.BuildVideoItem(options);

                if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
                {
                    if (streamInfo != null)
                    {
                        streamInfo.PlaySessionId           = playSessionId;
                        streamInfo.StartPositionTicks      = startTimeTicks;
                        mediaSource.TranscodingUrl         = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
                        mediaSource.TranscodingUrl        += "&allowVideoStreamCopy=false";
                        mediaSource.TranscodingUrl        += "&allowAudioStreamCopy=false";
                        mediaSource.TranscodingContainer   = streamInfo.Container;
                        mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;

                        // Do this after the above so that StartPositionTicks is set
                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                    }
                }
                else
                {
                    if (streamInfo != null)
                    {
                        streamInfo.PlaySessionId = playSessionId;

                        if (streamInfo.PlayMethod == PlayMethod.Transcode)
                        {
                            streamInfo.StartPositionTicks = startTimeTicks;
                            mediaSource.TranscodingUrl    = streamInfo.ToUrl("-", auth.Token).TrimStart('-');

                            if (!allowVideoStreamCopy)
                            {
                                mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
                            }
                            if (!allowAudioStreamCopy)
                            {
                                mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
                            }
                            mediaSource.TranscodingContainer   = streamInfo.Container;
                            mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
                        }

                        if (!allowAudioStreamCopy)
                        {
                            mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
                        }

                        mediaSource.TranscodingContainer   = streamInfo.Container;
                        mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;

                        // Do this after the above so that StartPositionTicks is set
                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
                    }
                }
            }

            foreach (var attachment in mediaSource.MediaAttachments)
            {
                attachment.DeliveryUrl = string.Format(
                    CultureInfo.InvariantCulture,
                    "/Videos/{0}/{1}/Attachments/{2}",
                    item.Id,
                    mediaSource.Id,
                    attachment.Index);
            }
        }
Esempio n. 17
0
        private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile, TimeSpan duration)
        {
            string videoArgs;

            if (EncodeVideo(mediaSource))
            {
                var maxBitrate = 25000000;
                videoArgs = string.Format(
                    "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41",
                    GetOutputSizeParam(),
                    maxBitrate.ToString(CultureInfo.InvariantCulture));
            }
            else
            {
                videoArgs = "-codec:v:0 copy";
            }

            videoArgs += " -fflags +genpts";

            var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);

            var flags = new List <string>();

            if (mediaSource.IgnoreDts)
            {
                flags.Add("+igndts");
            }
            if (mediaSource.IgnoreIndex)
            {
                flags.Add("+ignidx");
            }
            if (mediaSource.GenPtsInput)
            {
                flags.Add("+genpts");
            }

            var inputModifier = "-async 1 -vsync -1";

            if (flags.Count > 0)
            {
                inputModifier += " -fflags " + string.Join("", flags.ToArray());
            }

            var videoStream  = mediaSource.VideoStream;
            var videoDecoder = videoStream == null ? null : new EncodingHelper(_mediaEncoder, _fileSystem, null).GetVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions());

            if (!string.IsNullOrWhiteSpace(videoDecoder))
            {
                inputModifier += " " + videoDecoder;
            }

            if (mediaSource.ReadAtNativeFramerate)
            {
                inputModifier += " -re";
            }

            if (mediaSource.RequiresLooping)
            {
                inputModifier += " -stream_loop -1";
            }

            var analyzeDurationSeconds = 5;
            var analyzeDuration        = " -analyzeduration " +
                                         (analyzeDurationSeconds * 1000000).ToString(CultureInfo.InvariantCulture);

            inputModifier += analyzeDuration;

            var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";

            //var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
            //    " -f mp4 -movflags frag_keyframe+empty_moov" :
            //    string.Empty;

            var outputParam = string.Empty;

            var commandLineArgs = string.Format("-i \"{0}\"{5} {2} -map_metadata -1 -threads 0 {3}{4}{6} -y \"{1}\"",
                                                inputTempFile,
                                                targetFile,
                                                videoArgs,
                                                GetAudioArgs(mediaSource),
                                                subtitleArgs,
                                                durationParam,
                                                outputParam);

            return(inputModifier + " " + commandLineArgs);
        }
Esempio n. 18
0
        /// <summary>
        /// Gets the state.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>StreamState.</returns>
        protected async Task <StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
        {
            ParseDlnaHeaders(request);

            if (!string.IsNullOrWhiteSpace(request.Params))
            {
                ParseParams(request);
            }

            ParseStreamOptions(request);

            var url = Request.PathInfo;

            if (string.IsNullOrEmpty(request.AudioCodec))
            {
                request.AudioCodec = EncodingHelper.InferAudioCodec(url);
            }

            var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
                                                                                * string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;

            var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
            {
                Request           = request,
                RequestedUrl      = url,
                UserAgent         = Request.UserAgent,
                EnableDlnaHeaders = enableDlnaHeaders
            };

            var auth = AuthorizationContext.GetAuthorizationInfo(Request);

            if (!auth.UserId.Equals(Guid.Empty))
            {
                state.User = UserManager.GetUserById(auth.UserId);
            }

            //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
            //    (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
            //    (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
            //{
            //    state.SegmentLength = 6;
            //}

            if (state.VideoRequest != null)
            {
                if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
                {
                    state.SupportedVideoCodecs    = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
                    state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
                }
            }

            if (!string.IsNullOrWhiteSpace(request.AudioCodec))
            {
                state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
                state.Request.AudioCodec   = state.SupportedAudioCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i))
                                             ?? state.SupportedAudioCodecs.FirstOrDefault();
            }

            if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
            {
                state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
                state.Request.SubtitleCodec   = state.SupportedSubtitleCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToSubtitleCodec(i))
                                                ?? state.SupportedSubtitleCodecs.FirstOrDefault();
            }

            var item = LibraryManager.GetItemById(request.Id);

            state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);

            //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
            //             item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
            //if (primaryImage != null)
            //{
            //    state.AlbumCoverPath = primaryImage.Path;
            //}

            MediaSourceInfo mediaSource = null;

            if (string.IsNullOrWhiteSpace(request.LiveStreamId))
            {
                var currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
                                 ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
                    : null;

                if (currentJob != null)
                {
                    mediaSource = currentJob.MediaSource;
                }

                if (mediaSource == null)
                {
                    var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList();

                    mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
                       ? mediaSources.First()
                       : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));

                    if (mediaSource == null && request.MediaSourceId.Equals(request.Id))
                    {
                        mediaSource = mediaSources.First();
                    }
                }
            }
            else
            {
                var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false);

                mediaSource = liveStreamInfo.Item1;
                state.DirectStreamProvider = liveStreamInfo.Item2;
            }

            var videoRequest = request as VideoStreamRequest;

            EncodingHelper.AttachMediaSourceInfo(state, mediaSource, url);

            var container = Path.GetExtension(state.RequestedUrl);

            if (string.IsNullOrEmpty(container))
            {
                container = request.Container;
            }

            if (string.IsNullOrEmpty(container))
            {
                container = request.Static ?
                            StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio) :
                            GetOutputFileExtension(state);
            }

            state.OutputContainer = (container ?? string.Empty).TrimStart('.');

            state.OutputAudioBitrate = EncodingHelper.GetAudioBitrateParam(state.Request, state.AudioStream);

            state.OutputAudioCodec = state.Request.AudioCodec;

            state.OutputAudioChannels = EncodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);

            if (videoRequest != null)
            {
                state.OutputVideoCodec   = state.VideoRequest.VideoCodec;
                state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);

                if (videoRequest != null)
                {
                    EncodingHelper.TryStreamCopy(state);
                }

                if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
                {
                    var resolution = ResolutionNormalizer.Normalize(
                        state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
                        state.VideoStream == null ? (int?)null : state.VideoStream.Width,
                        state.VideoStream == null ? (int?)null : state.VideoStream.Height,
                        state.OutputVideoBitrate.Value,
                        state.VideoStream == null ? null : state.VideoStream.Codec,
                        state.OutputVideoCodec,
                        videoRequest.MaxWidth,
                        videoRequest.MaxHeight);

                    videoRequest.MaxWidth  = resolution.MaxWidth;
                    videoRequest.MaxHeight = resolution.MaxHeight;
                }

                ApplyDeviceProfileSettings(state);
            }
            else
            {
                ApplyDeviceProfileSettings(state);
            }

            var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
                ? GetOutputFileExtension(state)
                : ("." + state.OutputContainer);

            var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();

            state.OutputFilePath = GetOutputFilePath(state, encodingOptions, ext);

            return(state);
        }
Esempio n. 19
0
        private bool RequiresExtraction(SubtitleStreamInfo stream, MediaSourceInfo mediaSource)
        {
            var originalStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Subtitle && i.Index == stream.Index);

            return(originalStream != null && !originalStream.IsExternal);
        }
Esempio n. 20
0
        private async Task <bool> DetectInterlaced(MediaSourceInfo video, MediaStream videoStream, string inputPath, string probeSizeArgument)
        {
            if (video.Protocol != MediaProtocol.File)
            {
                return(false);
            }

            var formats = (video.Container ?? string.Empty).Split(',').ToList();

            // Take a shortcut and limit this to containers that are likely to have interlaced content
            if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) &&
                !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) &&
                !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) &&
                !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) &&
                !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase))
            {
                return(false);
            }

            var args = "{0} -i {1} -map 0:v:{2} -filter:v idet -frames:v 500 -an -f null /dev/null";

            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    CreateNoWindow  = true,
                    UseShellExecute = false,

                    // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    RedirectStandardInput  = true,
                    FileName  = FFMpegPath,
                    Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(),

                    WindowStyle = ProcessWindowStyle.Hidden,
                    ErrorDialog = false
                },

                EnableRaisingEvents = true
            };

            _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
            var idetFoundInterlaced = false;

            using (var processWrapper = new ProcessWrapper(process, this, _logger))
            {
                try
                {
                    StartProcess(processWrapper);
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error starting ffprobe", ex);

                    throw;
                }

                try
                {
                    process.BeginOutputReadLine();

                    using (var reader = new StreamReader(process.StandardError.BaseStream))
                    {
                        while (!reader.EndOfStream)
                        {
                            var line = await reader.ReadLineAsync().ConfigureAwait(false);

                            if (line.StartsWith("[Parsed_idet", StringComparison.OrdinalIgnoreCase))
                            {
                                var idetResult = AnalyzeIdetResult(line);

                                if (idetResult.HasValue)
                                {
                                    if (!idetResult.Value)
                                    {
                                        return(false);
                                    }

                                    idetFoundInterlaced = true;
                                }
                            }
                        }
                    }
                }
                catch
                {
                    StopProcess(processWrapper, 100, true);

                    throw;
                }
            }

            return(idetFoundInterlaced);
        }
Esempio n. 21
0
        protected virtual MediaSourceInfo CreateMediaSourceInfo(TunerHostInfo info, ChannelInfo channel)
        {
            var           path     = channel.Path;
            MediaProtocol protocol = MediaProtocol.File;

            if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
            {
                protocol = MediaProtocol.Http;
            }
            else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
            {
                protocol = MediaProtocol.Rtmp;
            }
            else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
            {
                protocol = MediaProtocol.Rtsp;
            }
            else if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
            {
                protocol = MediaProtocol.Udp;
            }
            else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
            {
                protocol = MediaProtocol.Rtmp;
            }

            Uri uri;
            var isRemote = true;

            if (Uri.TryCreate(path, UriKind.Absolute, out uri))
            {
                isRemote = !_networkManager.IsInLocalNetwork(uri.Host);
            }

            var supportsDirectPlay = !info.EnableStreamLooping && info.TunerCount == 0;

            var httpHeaders = new Dictionary <string, string>();

            // Use user-defined user-agent. If there isn't one, make it look like a browser.
            httpHeaders["User-Agent"] = string.IsNullOrWhiteSpace(info.UserAgent) ?
                                        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.85 Safari/537.36" :
                                        info.UserAgent;

            var mediaSource = new MediaSourceInfo
            {
                Path         = path,
                Protocol     = protocol,
                MediaStreams = new List <MediaStream>
                {
                    new MediaStream
                    {
                        Type = MediaStreamType.Video,
                        // Set the index to -1 because we don't know the exact index of the video stream within the container
                        Index        = -1,
                        IsInterlaced = true
                    },
                    new MediaStream
                    {
                        Type = MediaStreamType.Audio,
                        // Set the index to -1 because we don't know the exact index of the audio stream within the container
                        Index = -1
                    }
                },
                RequiresOpening = true,
                RequiresClosing = true,
                RequiresLooping = info.EnableStreamLooping,

                ReadAtNativeFramerate = false,

                Id = channel.Path.GetMD5().ToString("N"),
                IsInfiniteStream = true,
                IsRemote         = isRemote,

                IgnoreDts          = true,
                SupportsDirectPlay = supportsDirectPlay,

                RequiredHttpHeaders = httpHeaders
            };

            mediaSource.InferTotalBitrate();

            return(mediaSource);
        }
Esempio n. 22
0
        private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, ChannelInfo channelInfo, string profile)
        {
            int?   width        = null;
            int?   height       = null;
            bool   isInterlaced = true;
            string videoCodec   = null;

            int?videoBitrate = null;

            var isHd = channelInfo.IsHD ?? true;

            if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase))
            {
                width        = 1280;
                height       = 720;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 2000000;
            }
            else if (string.Equals(profile, "heavy", StringComparison.OrdinalIgnoreCase))
            {
                width        = 1920;
                height       = 1080;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 15000000;
            }
            else if (string.Equals(profile, "internet720", StringComparison.OrdinalIgnoreCase))
            {
                width        = 1280;
                height       = 720;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 8000000;
            }
            else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase))
            {
                width        = 960;
                height       = 540;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 2500000;
            }
            else if (string.Equals(profile, "internet480", StringComparison.OrdinalIgnoreCase))
            {
                width        = 848;
                height       = 480;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 2000000;
            }
            else if (string.Equals(profile, "internet360", StringComparison.OrdinalIgnoreCase))
            {
                width        = 640;
                height       = 360;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 1500000;
            }
            else if (string.Equals(profile, "internet240", StringComparison.OrdinalIgnoreCase))
            {
                width        = 432;
                height       = 240;
                isInterlaced = false;
                videoCodec   = "h264";
                videoBitrate = 1000000;
            }
            else
            {
                // This is for android tv's 1200 condition. Remove once not needed anymore so that we can avoid possible side effects of dummying up this data
                if (isHd)
                {
                    width  = 1920;
                    height = 1080;
                }
            }

            if (string.IsNullOrWhiteSpace(videoCodec))
            {
                videoCodec = channelInfo.VideoCodec;
            }

            string audioCodec = channelInfo.AudioCodec;

            videoBitrate ??= isHd ? 15000000 : 2000000;

            int?audioBitrate = isHd ? 448000 : 192000;

            // normalize
            if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase))
            {
                videoCodec = "mpeg2video";
            }

            string nal = null;

            if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
            {
                nal = "0";
            }

            var url = GetApiUrl(info);

            var id = profile;

            if (string.IsNullOrWhiteSpace(id))
            {
                id = "native";
            }

            id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture);

            var mediaSource = new MediaSourceInfo
            {
                Path         = url,
                Protocol     = MediaProtocol.Udp,
                MediaStreams = new MediaStream[]
                {
                    new MediaStream
                    {
                        Type = MediaStreamType.Video,
                        // Set the index to -1 because we don't know the exact index of the video stream within the container
                        Index         = -1,
                        IsInterlaced  = isInterlaced,
                        Codec         = videoCodec,
                        Width         = width,
                        Height        = height,
                        BitRate       = videoBitrate,
                        NalLengthSize = nal
                    },
                    new MediaStream
                    {
                        Type = MediaStreamType.Audio,
                        // Set the index to -1 because we don't know the exact index of the audio stream within the container
                        Index   = -1,
                        Codec   = audioCodec,
                        BitRate = audioBitrate
                    }
                },
                RequiresOpening = true,
                RequiresClosing = true,
                BufferMs        = 0,
                Container       = "ts",
                Id = id,
                SupportsDirectPlay   = false,
                SupportsDirectStream = true,
                SupportsTranscoding  = true,
                IsInfiniteStream     = true,
                IgnoreDts            = true,
                // IgnoreIndex = true,
                // ReadAtNativeFramerate = true
            };

            mediaSource.InferTotalBitrate();

            return(mediaSource);
        }
Esempio n. 23
0
        public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, string cacheKey, bool addProbeDelay, CancellationToken cancellationToken)
        {
            var originalRuntime = mediaSource.RunTimeTicks;

            var now = DateTime.UtcNow;

            MediaInfo mediaInfo     = null;
            var       cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json");

            if (!string.IsNullOrEmpty(cacheKey))
            {
                try
                {
                    mediaInfo = _json.DeserializeFromFile <MediaInfo>(cacheFilePath);

                    //_logger.LogDebug("Found cached media info");
                }
                catch
                {
                }
            }

            if (mediaInfo == null)
            {
                if (addProbeDelay)
                {
                    var delayMs = mediaSource.AnalyzeDurationMs ?? 0;
                    delayMs = Math.Max(3000, delayMs);
                    if (delayMs > 0)
                    {
                        _logger.LogInformation("Waiting {0}ms before probing the live stream", delayMs);
                        await Task.Delay(delayMs, cancellationToken).ConfigureAwait(false);
                    }
                }

                mediaSource.AnalyzeDurationMs = 3000;

                mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
                {
                    MediaSource     = mediaSource,
                    MediaType       = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
                    ExtractChapters = false
                }, cancellationToken).ConfigureAwait(false);

                if (cacheFilePath != null)
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                    _json.SerializeToFile(mediaInfo, cacheFilePath);

                    //_logger.LogDebug("Saved media info to {0}", cacheFilePath);
                }
            }

            var mediaStreams = mediaInfo.MediaStreams;

            if (!string.IsNullOrEmpty(cacheKey))
            {
                var newList = new List <MediaStream>();
                newList.AddRange(mediaStreams.Where(i => i.Type == MediaStreamType.Video).Take(1));
                newList.AddRange(mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Take(1));

                foreach (var stream in newList)
                {
                    stream.Index    = -1;
                    stream.Language = null;
                }

                mediaStreams = newList;
            }

            _logger.LogInformation("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));

            mediaSource.Bitrate       = mediaInfo.Bitrate;
            mediaSource.Container     = mediaInfo.Container;
            mediaSource.Formats       = mediaInfo.Formats;
            mediaSource.MediaStreams  = mediaStreams;
            mediaSource.RunTimeTicks  = mediaInfo.RunTimeTicks;
            mediaSource.Size          = mediaInfo.Size;
            mediaSource.Timestamp     = mediaInfo.Timestamp;
            mediaSource.Video3DFormat = mediaInfo.Video3DFormat;
            mediaSource.VideoType     = mediaInfo.VideoType;

            mediaSource.DefaultSubtitleStreamIndex = null;

            // Null this out so that it will be treated like a live stream
            if (!originalRuntime.HasValue)
            {
                mediaSource.RunTimeTicks = null;
            }

            var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);

            if (audioStream == null || audioStream.Index == -1)
            {
                mediaSource.DefaultAudioStreamIndex = null;
            }
            else
            {
                mediaSource.DefaultAudioStreamIndex = audioStream.Index;
            }

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

            if (videoStream != null)
            {
                if (!videoStream.BitRate.HasValue)
                {
                    var width = videoStream.Width ?? 1920;

                    if (width >= 3000)
                    {
                        videoStream.BitRate = 30000000;
                    }

                    else if (width >= 1900)
                    {
                        videoStream.BitRate = 20000000;
                    }

                    else if (width >= 1200)
                    {
                        videoStream.BitRate = 8000000;
                    }

                    else if (width >= 700)
                    {
                        videoStream.BitRate = 2000000;
                    }
                }

                // This is coming up false and preventing stream copy
                videoStream.IsAVC = null;
            }

            mediaSource.AnalyzeDurationMs = 3000;

            // Try to estimate this
            mediaSource.InferTotalBitrate(true);
        }
Esempio n. 24
0
 private void AttachMediaStreamInfo(EncodingJob state,
                                    MediaSourceInfo mediaSource,
                                    EncodingJobOptions videoRequest)
 {
     EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest);
 }
Esempio n. 25
0
 public Task <string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat?threedFormat, TimeSpan?offset, CancellationToken cancellationToken)
 {
     return(ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, cancellationToken));
 }
Esempio n. 26
0
        public async Task <bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList <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 inputPath = video.Path;

                            Directory.CreateDirectory(Path.GetDirectoryName(path));

                            var container   = video.Container;
                            var mediaSource = new MediaSourceInfo
                            {
                                VideoType = video.VideoType,
                                IsoType   = video.IsoType,
                                Protocol  = video.PathProtocol.Value,
                            };

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

                            File.Copy(tempFile, path, true);

                            try
                            {
                                _fileSystem.DeleteFile(tempFile);
                            }
                            catch (IOException ex)
                            {
                                _logger.LogError(ex, "Error deleting temporary chapter image encoding file {Path}", tempFile);
                            }

                            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, chapters);
            }

            DeleteDeadImages(currentImages, chapters);

            return(success);
        }
Esempio n. 27
0
        private async Task <string> ExtractImage(
            string inputFile,
            string container,
            MediaStream videoStream,
            int?imageStreamIndex,
            MediaSourceInfo mediaSource,
            bool isAudio,
            Video3DFormat?threedFormat,
            TimeSpan?offset,
            CancellationToken cancellationToken)
        {
            var inputArgument = GetInputArgument(inputFile, mediaSource);

            if (isAudio)
            {
                if (imageStreamIndex.HasValue && imageStreamIndex.Value > 0)
                {
                    // It seems for audio files we need to subtract 1 (for the audio stream??)
                    imageStreamIndex = imageStreamIndex.Value - 1;
                }
            }
            else
            {
                // The failure of HDR extraction usually occurs when using custom ffmpeg that does not contain the zscale filter.
                try
                {
                    return(await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, true, cancellationToken).ConfigureAwait(false));
                }
                catch (ArgumentException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "I-frame or HDR image extraction failed, will attempt with I-frame extraction disabled. Input: {Arguments}", inputArgument);
                }

                try
                {
                    return(await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, true, cancellationToken).ConfigureAwait(false));
                }
                catch (ArgumentException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "HDR image extraction failed, will fallback to SDR image extraction. Input: {Arguments}", inputArgument);
                }

                try
                {
                    return(await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, false, cancellationToken).ConfigureAwait(false));
                }
                catch (ArgumentException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {Arguments}", inputArgument);
                }
            }

            return(await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, false, cancellationToken).ConfigureAwait(false));
        }
Esempio n. 28
0
 public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
 {
     return(Path.ChangeExtension(targetFile, ".mp4"));
 }
Esempio n. 29
0
        private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile, TimeSpan duration)
        {
            string videoArgs;

            if (EncodeVideo(mediaSource))
            {
                const int MaxBitrate = 25000000;
                videoArgs = string.Format(
                    CultureInfo.InvariantCulture,
                    "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41",
                    GetOutputSizeParam(),
                    MaxBitrate);
            }
            else
            {
                videoArgs = "-codec:v:0 copy";
            }

            videoArgs += " -fflags +genpts";

            var flags = new List <string>();

            if (mediaSource.IgnoreDts)
            {
                flags.Add("+igndts");
            }

            if (mediaSource.IgnoreIndex)
            {
                flags.Add("+ignidx");
            }

            if (mediaSource.GenPtsInput)
            {
                flags.Add("+genpts");
            }

            var inputModifier = "-async 1 -vsync -1";

            if (flags.Count > 0)
            {
                inputModifier += " -fflags " + string.Join(string.Empty, flags);
            }

            if (mediaSource.ReadAtNativeFramerate)
            {
                inputModifier += " -re";
            }

            if (mediaSource.RequiresLooping)
            {
                inputModifier += " -stream_loop -1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2";
            }

            var analyzeDurationSeconds = 5;
            var analyzeDuration        = " -analyzeduration " +
                                         (analyzeDurationSeconds * 1000000).ToString(CultureInfo.InvariantCulture);

            inputModifier += analyzeDuration;

            var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";

            // var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
            //    " -f mp4 -movflags frag_keyframe+empty_moov" :
            //    string.Empty;

            var outputParam = string.Empty;

            var threads         = EncodingHelper.GetNumberOfThreads(null, _serverConfigurationManager.GetEncodingOptions(), null);
            var commandLineArgs = string.Format(
                CultureInfo.InvariantCulture,
                "-i \"{0}\" {2} -map_metadata -1 -threads {6} {3}{4}{5} -y \"{1}\"",
                inputTempFile,
                targetFile,
                videoArgs,
                GetAudioArgs(mediaSource),
                subtitleArgs,
                outputParam,
                threads);

            return(inputModifier + " " + commandLineArgs);
        }
Esempio n. 30
0
        private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
        {
            _targetPath = targetFile;
            _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));

            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    CreateNoWindow  = true,
                    UseShellExecute = false,

                    // Must consume both stdout and stderr or deadlocks may occur
                    //RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true,

                    FileName  = _mediaEncoder.EncoderPath,
                    Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),

                    WindowStyle = ProcessWindowStyle.Hidden,
                    ErrorDialog = false
                },

                EnableRaisingEvents = true
            };

            _process = process;

            var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;

            _logger.Info(commandLineLogMessage);

            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");

            _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));

            // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
            _logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);

            var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);

            _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);

            process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);

            process.Start();

            cancellationToken.Register(Stop);

            // MUST read both stdout and stderr asynchronously or a deadlock may occurr
            //process.BeginOutputReadLine();

            onStarted();

            // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
            StartStreamingLog(process.StandardError.BaseStream, _logFileStream);

            _logger.Info("ffmpeg recording process started for {0}", _targetPath);

            return(_taskCompletionSource.Task);
        }