Ejemplo n.º 1
0
        private static async Task <bool> SendSegmentAsync(string fileName, IOwinContext context, StreamItem streamItem)
        {
            if (!string.IsNullOrEmpty(fileName) && streamItem.StreamContext is TranscodeContext tc)
            {
                using (await streamItem.RequestBusyLockAsync(SendDataCancellation.Token))
                {
                    var segment = await MediaConverter.GetSegmentFileAsync((VideoTranscoding)streamItem.TranscoderObject.TranscodingParameter, tc, fileName);

                    if (segment != null)
                    {
                        if (segment.Value.ContainerEnum is VideoContainer)
                        {
                            VideoTranscoding video    = (VideoTranscoding)streamItem.TranscoderObject.TranscodingParameter;
                            List <string>    profiles = ProfileMime.ResolveVideoProfile((VideoContainer)segment.Value.ContainerEnum, video.TargetVideoCodec, video.TargetAudioCodec, EncodingProfile.Unknown, 0, 0, 0, 0, 0, 0, Timestamp.None);
                            string           mime     = "video/unknown";
                            ProfileMime.FindCompatibleMime(streamItem.Profile, profiles, ref mime);
                            context.Response.ContentType = mime;
                        }
                        else if (segment.Value.ContainerEnum is SubtitleCodec)
                        {
                            context.Response.ContentType = SubtitleHelper.GetSubtitleMime((SubtitleCodec)segment.Value.ContainerEnum);
                        }
                        bool onlyHeaders = context.Request.Method == "HEAD" || context.Response.StatusCode == (int)HttpStatusCode.NotModified;
                        Logger.Debug("RetrieveStream: Sending file header only: {0}", onlyHeaders.ToString());

                        await SendWholeFileAsync(context, segment.Value.FileData, onlyHeaders);

                        // Close the Stream so that FFMpeg can replace the playlist file
                        segment.Value.FileData.Dispose();
                        return(true);
                    }
                }
            }
            return(false);
        }
Ejemplo n.º 2
0
        public static bool AreMultipleAudioStreamsSupported(VideoTranscoding video)
        {
            if (!video.TargetAudioMultiTrackSupport)
            {
                return(false);
            }

            if (video.TargetVideoContainer == VideoContainer.Avi)
            {
                return(true);
            }
            if (video.TargetVideoContainer == VideoContainer.Matroska)
            {
                return(true);
            }
            if (video.TargetVideoContainer == VideoContainer.Mpeg2Ts)
            {
                return(true);
            }
            if (video.TargetVideoContainer == VideoContainer.Mp4)
            {
                return(true);
            }
            if (video.TargetVideoContainer == VideoContainer.Asf)
            {
                return(true);
            }

            return(false);
        }
Ejemplo n.º 3
0
        public static bool IsVideoStreamChanged(VideoTranscoding video)
        {
            bool notChanged = true;

            notChanged &= video.TargetForceVideoTranscoding == false;
            notChanged &= (video.TargetSubtitleSupport == SubtitleSupport.None || video.PreferredSourceSubtitles.Any() == false || video.TargetSubtitleSupport != SubtitleSupport.HardCoded);
            notChanged &= (video.TargetVideoCodec == VideoCodec.Unknown || video.TargetVideoCodec == video.SourceVideoStream.Codec);
            notChanged &= IsVideoDimensionChanged(video) == false;
            notChanged &= (!video.TargetVideoBitrate.HasValue || video.TargetVideoBitrate <= 0);

            return(notChanged == false);
        }
Ejemplo n.º 4
0
        public static bool IsAudioStreamChanged(int audioStreamIndex, BaseTranscoding media)
        {
            AudioCodec sourceCodec     = AudioCodec.Unknown;
            AudioCodec targetCodec     = AudioCodec.Unknown;
            long?      sourceBitrate   = 0;
            long?      targetBitrate   = 0;
            long?      sourceFrequency = 0;
            long?      targetFrequency = 0;

            if (media is VideoTranscoding)
            {
                VideoTranscoding video = (VideoTranscoding)media;
                sourceCodec     = video.SourceAudioStreams.First(s => s.StreamIndex == audioStreamIndex).Codec;
                sourceBitrate   = video.SourceAudioStreams.First(s => s.StreamIndex == audioStreamIndex).Bitrate;
                sourceFrequency = video.SourceAudioStreams.First(s => s.StreamIndex == audioStreamIndex).Frequency;
                targetCodec     = video.TargetAudioCodec;
                targetBitrate   = video.TargetAudioBitrate;
                targetFrequency = video.TargetAudioFrequency;
            }
            if (media is AudioTranscoding)
            {
                AudioTranscoding audio = (AudioTranscoding)media;
                sourceCodec     = audio.SourceAudioCodec;
                sourceBitrate   = audio.SourceAudioBitrate;
                sourceFrequency = audio.SourceAudioFrequency;
                targetCodec     = audio.TargetAudioCodec;
                targetBitrate   = audio.TargetAudioBitrate;
                targetFrequency = audio.TargetAudioFrequency;
            }

            bool notChanged = true;

            notChanged &= (targetCodec == AudioCodec.Unknown || sourceCodec == targetCodec);
            notChanged &= (!targetBitrate.HasValue || sourceBitrate == targetBitrate);
            notChanged &= (!targetFrequency.HasValue || sourceFrequency == targetFrequency);

            return(notChanged == false);
        }
Ejemplo n.º 5
0
        public static Task <Stream> CreateSubsPlaylistAsync(VideoTranscoding video, long startSegment)
        {
            if (ServiceRegistration.IsRegistered <IMediaConverter>())
            {
                IMediaConverter converter       = ServiceRegistration.Get <IMediaConverter>();
                StringBuilder   palylistBuilder = new StringBuilder();

                using (StringWriter writer = new StringWriter(palylistBuilder))
                {
                    writer.WriteLine("#EXTM3U");
                    writer.WriteLine("#EXT-X-VERSION:3");
                    writer.WriteLine("#EXT-X-ALLOW-CACHE:NO");
                    writer.WriteLine("#EXT-X-TARGETDURATION:" + converter.HLSSegmentTimeInSeconds);
                    writer.WriteLine("#EXT-X-MEDIA-SEQUENCE:0");
                    writer.WriteLine();

                    double remainingDuration = video.SourceMediaDuration.TotalSeconds;
                    remainingDuration -= (Convert.ToDouble(startSegment) * Convert.ToDouble(converter.HLSSegmentTimeInSeconds));
                    while (remainingDuration > 0)
                    {
                        double segmentTime = remainingDuration >= converter.HLSSegmentTimeInSeconds ? converter.HLSSegmentTimeInSeconds : remainingDuration;
                        writer.WriteLine("#EXTINF:" + segmentTime.ToString("0.000000", CultureInfo.InvariantCulture) + ",");
                        writer.WriteLine(URL_PLACEHOLDER + "playlist" + startSegment.ToString("0") + ".vtt");
                        writer.WriteLine();

                        startSegment++;
                        remainingDuration -= converter.HLSSegmentTimeInSeconds;
                    }

                    writer.WriteLine("#EXT-X-ENDLIST");
                }

                var memStream = new MemoryStream(Encoding.UTF8.GetBytes(palylistBuilder.ToString()));
                memStream.Position = 0;
                return(Task.FromResult <Stream>(memStream));
            }
            return(Task.FromResult <Stream>(null));
        }
        public static bool IsSoftCodedSubtitleAvailable(DlnaMediaItem dlnaItem, EndPointSettings client)
        {
            if (client.Profile.MediaTranscoding.SubtitleSettings.SubtitleMode != SubtitleSupport.SoftCoded)
            {
                return(false);
            }
            if (dlnaItem.IsTranscoded && dlnaItem.IsVideo)
            {
                VideoTranscoding video = (VideoTranscoding)dlnaItem.TranscodingParameter;
                return(video?.SourceSubtitles.Count > 0);
            }
            else if (dlnaItem.IsVideo)
            {
                VideoTranscoding subtitleVideo = (VideoTranscoding)dlnaItem.SubtitleTranscodingParameter;
                if (subtitleVideo?.SourceSubtitles.Count > 0)
                {
                    return(true);
                }

                return(dlnaItem.Subtitles.Count > 0);
            }
            return(false);
        }
Ejemplo n.º 7
0
        public async Task <LiveTvMediaItem> InitializeChannel(int channelId)
        {
            IsLive         = true;
            MediaItemId    = Guid.Empty;
            MediaItemTitle = "Channel " + channelId;

            LiveTvMediaItem item = new LiveTvMediaItem(Guid.Empty);
            var             info = await MediaAnalyzer.ParseChannelStreamAsync(channelId, item);

            if (info == null)
            {
                Logger.Warn("MediaServer: Channel {0} couldn't be analyzed", channelId);
                return(null);
            }

            if (item.Aspects.ContainsKey(AudioAspect.ASPECT_ID))
            {
                IsAudio = true;
            }
            else if (item.Aspects.ContainsKey(VideoAspect.ASPECT_ID))
            {
                IsVideo = true;
            }
            else
            {
                Logger.Warn("MediaServer: Channel {0} contains no required aspect information", channelId);
                return(null);
            }

            if (MediaItemAspect.TryGetAttribute(item.Aspects, MediaAspect.ATTR_TITLE, out string title))
            {
                MediaItemTitle = title;
            }
            _edition = info.Metadata.Min(m => m.Key);

            string transcodeId = Guid.NewGuid().ToString() + "_" + Client.Profile.ID;

            if (MediaServerPlugin.Settings.TranscodingAllowed)
            {
                if (IsAudio)
                {
                    AudioTranscoding audio = TranscodeProfileManager.GetAudioTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                         info, _edition, IsLive, transcodeId);
                    TranscodingParameter = audio;
                }
                else if (IsVideo)
                {
                    VideoTranscoding video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                         info, _edition, Client.PreferredAudioLanguages, IsLive, transcodeId);
                    if (video != null)
                    {
                        if (video.TargetVideoContainer == VideoContainer.Hls)
                        {
                            IsSegmented = true;
                        }
                        if (MediaServerPlugin.Settings.HardcodedSubtitlesAllowed == false)
                        {
                            video.TargetSubtitleSupport = SubtitleSupport.None;
                        }
                    }
                    TranscodingParameter = video;
                }
            }

            if (TranscodingParameter == null)
            {
                if (IsVideo)
                {
                    TranscodingParameter = TranscodeProfileManager.GetLiveVideoTranscoding(info, Client.PreferredAudioLanguages, transcodeId);
                }
                else if (IsAudio)
                {
                    TranscodingParameter = TranscodeProfileManager.GetLiveAudioTranscoding(info, transcodeId);
                }
            }

            AssignDlnaMetadata(info, _edition);

            return(item);
        }
Ejemplo n.º 8
0
        public async Task Initialize(Guid?userId, MediaItem item, int?edition, int?audioId = null, int?subtitleId = null)
        {
            MediaItemId = item.MediaItemId;

            var info = await MediaAnalyzer.ParseMediaItemAsync(item);

            if (info == null)
            {
                Logger.Warn("MP2Extended: Mediaitem {0} couldn't be analyzed", item.MediaItemId);
                return;
            }

            int?          audioStreamId         = null;
            List <string> preferredAudioLang    = new List <string>();
            List <string> preferredSubtitleLang = new List <string>();
            await ResourceAccessUtils.AddPreferredLanguagesAsync(userId, preferredAudioLang, preferredSubtitleLang);

            if (audioId.HasValue)
            {
                if (audioId.Value >= EDITION_OFFSET)
                {
                    edition       = (audioId.Value / EDITION_OFFSET) - 1;
                    audioStreamId = audioId.Value - ((audioId.Value / EDITION_OFFSET) * EDITION_OFFSET);
                }
                else
                {
                    audioStreamId = audioId;
                }
            }

            if (item.HasEditions && !edition.HasValue)
            {
                //Find edition with best matching audio that doesn't require transcoding
                int  currentPriority            = -1;
                bool currentRequiresTranscoding = false;
                foreach (var checkEdition in info.Metadata.Keys)
                {
                    for (int idx = 0; idx < info.Audio[checkEdition].Count; idx++)
                    {
                        VideoTranscoding video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Profile.ID,
                                                                                             info, checkEdition, preferredAudioLang, IsLive, "");

                        bool requiresTranscoding = video != null;

                        for (int priority = 0; priority < preferredAudioLang.Count; priority++)
                        {
                            if (preferredAudioLang[priority].Equals(info.Audio[checkEdition][idx].Language, StringComparison.InvariantCultureIgnoreCase) == true)
                            {
                                if (currentPriority == -1 || priority < currentPriority || (!requiresTranscoding && currentRequiresTranscoding && priority == currentPriority))
                                {
                                    currentPriority            = priority;
                                    currentRequiresTranscoding = requiresTranscoding;
                                    audioStreamId = info.Audio[checkEdition][idx].StreamIndex;
                                    _edition      = checkEdition;
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                //Assign first edition
                _edition = info.Metadata.Min(m => m.Key);
            }

            if (item.Aspects.ContainsKey(AudioAspect.ASPECT_ID))
            {
                IsAudio = true;
            }
            else if (item.Aspects.ContainsKey(ImageAspect.ASPECT_ID))
            {
                IsImage = true;
            }
            else if (item.Aspects.ContainsKey(VideoAspect.ASPECT_ID))
            {
                IsVideo = true;
            }
            else
            {
                Logger.Warn("MP2Extended: Mediaitem {0} contains no required aspect information", item.MediaItemId);
                return;
            }

            string transcodeId = item.MediaItemId.ToString() + "_" + Profile.ID;

            if (IsLive)
            {
                transcodeId = Guid.NewGuid().ToString() + "_" + Profile.ID;
            }

            if (MP2Extended.Settings.TranscodingAllowed)
            {
                if (IsAudio)
                {
                    AudioTranscoding audio = TranscodeProfileManager.GetAudioTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Profile.ID,
                                                                                         info, _edition, IsLive, transcodeId);
                    TranscodingParameter = audio;
                }
                else if (IsImage)
                {
                    ImageTranscoding image = TranscodeProfileManager.GetImageTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Profile.ID,
                                                                                         info, _edition, transcodeId);
                    TranscodingParameter = image;
                }
                else if (IsVideo)
                {
                    VideoTranscoding video;
                    if (audioStreamId.HasValue)
                    {
                        video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Profile.ID,
                                                                            info, _edition, audioStreamId.Value, subtitleId, IsLive, transcodeId);
                    }
                    else
                    {
                        video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Profile.ID,
                                                                            info, _edition, preferredAudioLang, IsLive, transcodeId);
                    }

                    if (video != null)
                    {
                        if (video.TargetVideoContainer == VideoContainer.Hls)
                        {
                            IsSegmented = true;
                        }
                        if (MP2Extended.Settings.HardcodedSubtitlesAllowed == false)
                        {
                            video.TargetSubtitleSupport = SubtitleSupport.None;
                        }
                    }
                    TranscodingParameter = video;
                }
            }

            if (TranscodingParameter == null)
            {
                if (IsLive)
                {
                    if (IsVideo)
                    {
                        if (audioStreamId.HasValue)
                        {
                            TranscodingParameter = TranscodeProfileManager.GetLiveVideoTranscoding(info, audioStreamId.Value, transcodeId);
                        }
                        else
                        {
                            TranscodingParameter = TranscodeProfileManager.GetLiveVideoTranscoding(info, preferredAudioLang, transcodeId);
                        }
                    }
                    else if (IsAudio)
                    {
                        TranscodingParameter = TranscodeProfileManager.GetLiveAudioTranscoding(info, transcodeId);
                    }
                }
                else if (IsVideo)
                {
                    VideoTranscoding video = TranscodeProfileManager.GetVideoSubtitleTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Profile.ID,
                                                                                                 info, _edition, IsLive, transcodeId);
                    if (video != null)
                    {
                        if (video.TargetVideoContainer == VideoContainer.Hls)
                        {
                            IsSegmented = true;
                        }
                        if (MP2Extended.Settings.HardcodedSubtitlesAllowed == false)
                        {
                            video.TargetSubtitleSupport = SubtitleSupport.None;
                        }
                    }
                    TranscodingParameter = video;
                }
            }

            AssignWebMetadata(info, _edition);
        }
Ejemplo n.º 9
0
        public static bool IsSquarePixelNeeded(VideoTranscoding video)
        {
            bool squarePixels = IsSquarePixel(video.SourceVideoStream.PixelAspectRatio);

            return((video.TargetVideoContainer == VideoContainer.Asf || video.TargetVideoContainer == VideoContainer.Flv) && squarePixels == false);
        }
Ejemplo n.º 10
0
 public static bool IsVideoDimensionChanged(VideoTranscoding video)
 {
     return(IsVideoHeightChangeNeeded(video.SourceVideoStream.Height, video.TargetVideoMaxHeight) ||
            IsVideoAspectRatioChanged(video.SourceVideoStream.Width, video.SourceVideoStream.Height, video.SourceVideoStream.PixelAspectRatio, video.TargetVideoAspectRatio) ||
            IsSquarePixelNeeded(video));
 }
        protected override async Task <bool> ConvertSubtitleFileAsync(string clientId, VideoTranscoding video, double timeStart, string transcodingFile, SubtitleStream sourceSubtitle, SubtitleStream res)
        {
            SubtitleCodec targetCodec = video.TargetSubtitleCodec;

            if (targetCodec == SubtitleCodec.Unknown)
            {
                targetCodec = sourceSubtitle.Codec;
            }

            string tempFile          = null;
            FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath)
            {
                TranscodeId = video.TranscodeId + "_sub", ClientId = clientId
            };

            if (string.IsNullOrEmpty(video.TranscoderBinPath) == false)
            {
                data.TranscoderBinPath = video.TranscoderBinPath;
            }
            if (string.IsNullOrEmpty(video.TranscoderArguments) == false)
            {
                // TODO: not sure if this is working
                data.TranscoderArguments = video.TranscoderArguments;
                data.InputMediaFilePaths.Add(0, res.SourcePath);
                data.InputArguments.Add(0, new List <string>());
            }
            else
            {
                tempFile = transcodingFile + ".tmp";
                res      = await ConvertSubtitleEncodingAsync(res, tempFile, video.TargetSubtitleCharacterEncoding).ConfigureAwait(false);

                // TODO: not sure if this is working
                _ffMpegCommandline.InitTranscodingParameters(false, new Dictionary <int, string> {
                    { 0, res.SourcePath }
                }, ref data);
                data.InputArguments[0].Add(string.Format("-f {0}", FFMpegGetSubtitleContainer.GetSubtitleContainer(sourceSubtitle.Codec)));
                if (timeStart > 0)
                {
                    data.OutputArguments.Add(string.Format(CultureInfo.InvariantCulture, "-ss {0:0.0}", timeStart));
                }

                res.Codec = targetCodec;
                string subtitleEncoder = "copy";
                if (res.Codec == SubtitleCodec.Unknown)
                {
                    res.Codec = SubtitleCodec.Ass;
                }

                if (sourceSubtitle.Codec != res.Codec)
                {
                    subtitleEncoder = FFMpegGetSubtitleContainer.GetSubtitleContainer(res.Codec);
                }

                string subtitleFormat = FFMpegGetSubtitleContainer.GetSubtitleContainer(res.Codec);
                data.OutputArguments.Add("-vn");
                data.OutputArguments.Add("-an");
                data.OutputArguments.Add(string.Format("-c:s {0}", subtitleEncoder));
                data.OutputArguments.Add(string.Format("-f {0}", subtitleFormat));
            }
            data.OutputFilePath = transcodingFile;

            _logger.Debug("FFMpegMediaConverter: Invoking transcoder to transcode subtitle file '{0}' for transcode '{1}'", res.SourcePath, data.TranscodeId);
            bool success = false;
            var  path    = ResourcePath.Deserialize(res.SourcePath);

            if (path.TryCreateLocalResourceAccessor(out var subRes))
            {
                using (var rah = new LocalFsResourceAccessorHelper(subRes))
                    using (var access = rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess())
                    {
                        var result = await FFMpegBinary.FFMpegExecuteWithResourceAccessAsync(rah.LocalFsResourceAccessor, data.TranscoderArguments, ProcessPriorityClass.Normal, _transcoderTimeout).ConfigureAwait(false);

                        success = result.Success;
                    }
            }
            if (success && File.Exists(transcodingFile) == true)
            {
                if (tempFile != null && File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }
                res.SourcePath = LocalFsResourceProviderBase.ToProviderPath(transcodingFile);
                return(true);
            }
            return(false);
        }
Ejemplo n.º 12
0
        public static Task <Stream> CreatePlaylistManifestAsync(VideoTranscoding video, SubtitleStream sub)
        {
            if (ServiceRegistration.IsRegistered <IMediaConverter>())
            {
                IMediaConverter         converter = ServiceRegistration.Get <IMediaConverter>();
                TranscodedVideoMetadata metaData  = converter.GetTranscodedVideoMetadata(video);

                double bitrate = 10000000;
                if (metaData.TargetVideoBitrate.HasValue && metaData.TargetAudioBitrate.HasValue)
                {
                    bitrate += metaData.TargetVideoBitrate.Value;
                    bitrate += metaData.TargetAudioBitrate.Value;
                    bitrate  = bitrate * 1024; //Bitrate in bits/s
                }

                int width  = 1920;
                int height = 1080;
                if (metaData.TargetVideoMaxHeight.HasValue && metaData.TargetVideoMaxWidth.HasValue)
                {
                    width  = metaData.TargetVideoMaxHeight.Value;
                    height = metaData.TargetVideoMaxWidth.Value;
                }

                string codec = "avc1.66.30,mp4a.40.2"; //H264 Baseline 3.0 and AAC
                if (metaData.TargetVideoCodec == VideoCodec.H264)
                {
                    codec = "avc1.";
                    if (metaData.TargetProfile == EncodingProfile.Baseline)
                    {
                        codec += "66.";
                    }
                    else if (metaData.TargetProfile == EncodingProfile.Main)
                    {
                        codec += "77.";
                    }
                    else //High
                    {
                        codec += "100.";
                    }
                    codec += ((metaData.TargetLevel ?? 0) * 10).ToString("0");

                    if (metaData.TargetAudioCodec == AudioCodec.Ac3)
                    {
                        codec += ",ac-3";
                    }
                    else
                    {
                        codec += ",mp4a.40.";
                        if (metaData.TargetAudioCodec == AudioCodec.Aac)
                        {
                            codec += "2";
                        }
                        else if (metaData.TargetAudioCodec == AudioCodec.Mp3)
                        {
                            codec += "34";
                        }
                        else //HE-ACC
                        {
                            codec += "5";
                        }
                    }
                }

                StringBuilder manifestBuilder = new StringBuilder();
                using (StringWriter writer = new StringWriter(manifestBuilder))
                {
                    writer.WriteLine("#EXTM3U");
                    writer.WriteLine();
                    if (sub != null)
                    {
                        CultureInfo culture = new CultureInfo(sub.Language);
                        writer.WriteLine(string.Format("#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE=\"{1}\",URI=\"{2}\"",
                                                       culture.DisplayName, culture.TwoLetterISOLanguageName.ToLowerInvariant(), URL_PLACEHOLDER + converter.HLSSubtitlePlayListName));
                        writer.WriteLine();
                    }
                    writer.WriteLine(string.Format("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH={0},RESOLUTION={1},CODECS=\"{2}\"{3}",
                                                   bitrate.ToString("0"), width + "x" + height, codec, sub != null ? ",SUBTITLES=\"subs\"" : ""));
                    writer.WriteLine(URL_PLACEHOLDER + converter.HLSMediaPlayListName);
                    writer.WriteLine();
                }

                var memStream = new MemoryStream(Encoding.UTF8.GetBytes(manifestBuilder.ToString()));
                memStream.Position = 0;
                return(Task.FromResult <Stream>(memStream));
            }
            return(Task.FromResult <Stream>(null));
        }
        protected override async Task <TranscodeContext> TranscodeVideoAsync(string clientId, VideoTranscoding video, double timeStart, double timeDuration, bool waitForBuffer)
        {
            FFMpegTranscodeContext context = new FFMpegTranscodeContext(_cacheEnabled, _cachePath);

            context.TargetDuration = video.SourceMediaDuration;
            if (timeStart == 0 && video.TargetIsLive == false && _cacheEnabled)
            {
                timeDuration    = 0;
                context.Partial = false;
            }
            else if (video.TargetVideoContainer == VideoContainer.Hls)
            {
                context.Partial = true;
            }
            else
            {
                video.TargetIsLive = true;
                context.Partial    = true;
            }
            if (video.TargetVideoContainer == VideoContainer.Unknown)
            {
                video.TargetVideoContainer = video.SourceVideoContainer;
            }

            bool          embeddedSupported = false;
            SubtitleCodec embeddedSubCodec  = SubtitleCodec.Unknown;

            if (video.TargetSubtitleSupport == SubtitleSupport.Embedded)
            {
                if (video.TargetVideoContainer == VideoContainer.Matroska)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.Ass;
                    video.TargetSubtitleCodec = SubtitleCodec.Ass;
                }
                else if (video.TargetVideoContainer == VideoContainer.Mp4)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.MovTxt;
                    video.TargetSubtitleCodec = SubtitleCodec.MovTxt;
                }
                else if (video.TargetVideoContainer == VideoContainer.Hls)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.WebVtt;
                    video.TargetSubtitleCodec = SubtitleCodec.WebVtt;
                }
                else if (video.TargetVideoContainer == VideoContainer.Avi)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.Srt;
                    video.TargetSubtitleCodec = SubtitleCodec.Srt;
                }
                //else if (video.TargetVideoContainer == VideoContainer.Mpeg2Ts)
                //{
                //  embeddedSupported = true;
                //  embeddedSubCodec = SubtitleCodec.DvbSub;
                //  video.TargetSubtitleCodec = SubtitleCodec.VobSub;
                //}
                else
                {
                    _logger.Debug("FFMpegMediaConverter: Container {0} does not support embedded subtitles", video.TargetVideoContainer);
                }
            }
            video.TargetSubtitleMime       = SubtitleHelper.GetSubtitleMime(video.TargetSubtitleCodec);
            video.PreferredSourceSubtitles = await GetSubtitlesAsync(clientId, video, timeStart).ConfigureAwait(false);

            string transcodingFile = GetTranscodingVideoFileName(video, timeStart, embeddedSupported);

            transcodingFile = Path.Combine(_cachePath, transcodingFile);

            if (File.Exists(transcodingFile))
            {
                //Use non-partial transcode if possible
                TranscodeContext existingContext = await GetExistingTranscodeContextAsync(clientId, video.TranscodeId).ConfigureAwait(false);

                if (existingContext != null)
                {
                    existingContext.TargetFile = transcodingFile;
                    if (existingContext.Stream == null)
                    {
                        existingContext.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    }
                    if (existingContext.CurrentDuration.TotalSeconds == 0)
                    {
                        double bitrate = 0;
                        if (video.TargetVideoBitrate.HasValue && video.TargetAudioBitrate.HasValue)
                        {
                            bitrate = video.TargetVideoBitrate.Value + video.TargetAudioBitrate.Value;
                        }
                        else if (video.SourceVideoStream.Bitrate.HasValue && video.SourceAudioStreams.Any(a => a.Bitrate > 0))
                        {
                            bitrate = video.SourceVideoStream.Bitrate.Value + video.SourceAudioStreams.Max(a => a.Bitrate ?? 0);
                        }
                        bitrate *= 1024; //Bitrate in bits/s
                        if (bitrate > 0)
                        {
                            long startByte = Convert.ToInt64((bitrate * timeStart) / 8.0);
                            if (existingContext.Stream.Length > startByte)
                            {
                                return(existingContext);
                            }
                        }
                    }
                    else
                    {
                        if (existingContext.CurrentDuration.TotalSeconds > timeStart)
                        {
                            return(existingContext);
                        }
                    }
                }
                else
                {
                    //Presume that it is a cached file
                    TouchFile(transcodingFile);
                    context.Partial    = false;
                    context.TargetFile = transcodingFile;
                    context.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    return(context);
                }
            }

            if (video.TargetVideoContainer == VideoContainer.Hls)
            {
                long requestedSegmentSequence = requestedSegmentSequence = Convert.ToInt64(timeStart / HLSSegmentTimeInSeconds);
                if (requestedSegmentSequence > 0)
                {
                    requestedSegmentSequence--; //1 segment file margin
                }
                string pathName    = FFMpegPlaylistManifest.GetPlaylistFolderFromTranscodeFile(_cachePath, transcodingFile);
                string playlist    = Path.Combine(pathName, PlaylistManifest.PLAYLIST_MANIFEST_FILE_NAME);
                string segmentFile = Path.Combine(pathName, requestedSegmentSequence.ToString("00000") + ".ts");
                if (File.Exists(playlist) == true && File.Exists(segmentFile) == true)
                {
                    //Use exisitng context if possible
                    TranscodeContext existingContext = await GetExistingTranscodeContextAsync(clientId, video.TranscodeId).ConfigureAwait(false);

                    if (existingContext != null)
                    {
                        if (existingContext.LastSegment > requestedSegmentSequence)
                        {
                            existingContext.TargetFile = playlist;
                            existingContext.SegmentDir = pathName;
                            if (existingContext.Stream == null)
                            {
                                existingContext.AssignStream(await GetFileStreamAsync(playlist).ConfigureAwait(false));
                            }
                            existingContext.HlsBaseUrl = video.HlsBaseUrl;
                            return(existingContext);
                        }
                    }
                    else
                    {
                        //Presume that it is a cached file
                        TouchDirectory(pathName);
                        context.Partial    = false;
                        context.TargetFile = playlist;
                        context.SegmentDir = pathName;
                        context.HlsBaseUrl = video.HlsBaseUrl;
                        context.AssignStream(await GetFileStreamAsync(playlist).ConfigureAwait(false));
                        return(context);
                    }
                }
            }

            FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath)
            {
                TranscodeId = video.TranscodeId, ClientId = clientId
            };

            if (string.IsNullOrEmpty(video.TranscoderBinPath) == false)
            {
                data.TranscoderBinPath = video.TranscoderBinPath;
            }
            if (string.IsNullOrEmpty(video.TranscoderArguments) == false)
            {
                data.ConcatedFileInput   = video.ConcatSourceMediaPaths;
                data.TranscoderArguments = video.TranscoderArguments;
                data.InputMediaFilePaths = video.SourceMediaPaths;
                if (video.PreferredSourceSubtitles != null)
                {
                    foreach (var mediaSourceIndex in video.PreferredSourceSubtitles.Keys)
                    {
                        foreach (var sub in video.PreferredSourceSubtitles[mediaSourceIndex])
                        {
                            if (string.IsNullOrEmpty(sub.SourcePath) == false)
                            {
                                data.AddSubtitle(mediaSourceIndex, sub.SourcePath);
                                context.TargetSubtitles.Add(sub.SourcePath);
                            }
                        }
                    }
                }
                data.OutputFilePath = transcodingFile;
                context.TargetFile  = transcodingFile;
            }
            else
            {
                data.Encoder = _ffMpegEncoderHandler.StartEncoding(video.TranscodeId, video.TargetVideoCodec);
                _ffMpegCommandline.InitTranscodingParameters(video.ConcatSourceMediaPaths, video.SourceMediaPaths, ref data);

                bool useX26XLib = video.TargetVideoCodec == VideoCodec.H264 || video.TargetVideoCodec == VideoCodec.H265;
                _ffMpegCommandline.AddTranscodingThreadsParameters(!useX26XLib, ref data);

                int subCopyStream = -1;
                if (video.PreferredSourceSubtitles.Any())
                {
                    if (video.FirstPreferredSourceSubtitle.IsEmbedded)
                    {
                        subCopyStream = video.FirstPreferredSourceSubtitle.StreamIndex;
                        _ffMpegCommandline.AddSubtitleCopyParameters(video.FirstPreferredSourceSubtitle, data);
                    }
                    else if (embeddedSupported)
                    {
                        foreach (int mediaSourceIndex in video.PreferredSourceSubtitles.Keys)
                        {
                            _ffMpegCommandline.AddSubtitleEmbeddingParameters(mediaSourceIndex, video.PreferredSourceSubtitles[mediaSourceIndex], embeddedSubCodec, timeStart, data);
                        }
                    }
                    else if (video.TargetSubtitleSupport != SubtitleSupport.SoftCoded)
                    {
                        video.TargetSubtitleSupport = SubtitleSupport.HardCoded; //Fallback to hardcoded subtitles
                        _logger.Debug("FFMpegMediaConverter: Soft subs not supported. Fallback to hardcoded subtitles");
                    }
                }
                else
                {
                    embeddedSupported = false;
                    data.OutputArguments.Add("-sn");
                }

                _ffMpegCommandline.AddTimeParameters(video, timeStart, timeDuration, data);
                _ffMpegCommandline.AddStreamMapParameters(video, data);

                FFMpegEncoderConfig encoderConfig = _ffMpegEncoderHandler.GetEncoderConfig(data.Encoder);
                _ffMpegCommandline.AddVideoParameters(video, data.TranscodeId, encoderConfig, data);
                _ffMpegCommandline.AddVideoAudioParameters(video, data);

                var result = await _ffMpegCommandline.AddTargetVideoFormatAndOutputFileParametersAsync(video, transcodingFile, timeStart, data).ConfigureAwait(false);

                context.TargetFile     = result.TranscodingFile;
                context.CurrentSegment = result.StartSegment;
                if (video.PreferredSourceSubtitles.Any())
                {
                    foreach (var sub in video.PreferredSourceSubtitles.SelectMany(s => s.Value))
                    {
                        if (string.IsNullOrEmpty(sub.SourcePath) == false)
                        {
                            context.TargetSubtitles.Add(sub.SourcePath);
                        }
                    }
                }
            }

            _logger.Info("FFMpegMediaConverter: Invoking transcoder to transcode video file '{0}' for transcode '{1}' with arguments '{2}'", video.SourceMediaPaths.First().Value, video.TranscodeId, String.Join(", ", data.OutputArguments.ToArray()));
            context.Start();
            context.AssignStream(await ExecuteTranscodingProcessAsync(data, context, waitForBuffer).ConfigureAwait(false));
            return(context);
        }
 protected override void GetVideoDimensions(VideoTranscoding video, out Size newSize, out Size newContentSize, out float newPixelAspectRatio, out bool pixelARChanged, out bool videoARChanged, out bool videoHeightChanged)
 {
     _ffMpegCommandline.GetVideoDimensions(video, out newSize, out newContentSize, out newPixelAspectRatio, out pixelARChanged, out videoARChanged, out videoHeightChanged);
 }
Ejemplo n.º 15
0
        private void AssignDlnaMetadata(MetadataContainer info, int edition)
        {
            if (info == null)
            {
                return;
            }

            List <string> profileList = new List <string>();

            if (TranscodingParameter == null)
            {
                Metadata  = info.Metadata[edition];
                Video     = info.Video[edition];
                Audio     = info.Audio[edition];
                Image     = info.Image[edition];
                Subtitles = info.Subtitles[edition];
            }
            else
            {
                if (IsImage)
                {
                    ImageTranscoding        image    = (ImageTranscoding)TranscodingParameter;
                    TranscodedImageMetadata metadata = MediaConverter.GetTranscodedImageMetadata(image);
                    Metadata = new MetadataStream
                    {
                        Mime = info.Metadata[edition].Mime,
                        ImageContainerType = metadata.TargetImageCodec,
                        Size = Client.EstimateTransodedSize ? info.Metadata[edition].Size : 0,
                    };
                    Image = new ImageStream
                    {
                        Height          = metadata.TargetMaxHeight,
                        Orientation     = metadata.TargetOrientation,
                        PixelFormatType = metadata.TargetPixelFormat,
                        Width           = metadata.TargetMaxWidth
                    };
                }
                else if (IsAudio)
                {
                    AudioTranscoding        audio    = (AudioTranscoding)TranscodingParameter;
                    TranscodedAudioMetadata metadata = MediaConverter.GetTranscodedAudioMetadata(audio);
                    Metadata = new MetadataStream
                    {
                        Mime = info.Metadata[edition].Mime,
                        AudioContainerType = metadata.TargetAudioContainer,
                        Bitrate            = metadata.TargetAudioBitrate > 0 ? metadata.TargetAudioBitrate : null,
                        Duration           = info.Metadata[edition].Duration,
                        Size = Client.EstimateTransodedSize ? (metadata.TargetAudioBitrate > 0 ? Convert.ToInt64((metadata.TargetAudioBitrate * 1024 * info.Metadata[edition].Duration) / 8.0) : (long?)null) : null,
                    };
                    AudioStream audioStream = new AudioStream();
                    audioStream.Bitrate   = metadata.TargetAudioBitrate;
                    audioStream.Channels  = metadata.TargetAudioChannels;
                    audioStream.Codec     = metadata.TargetAudioCodec;
                    audioStream.Frequency = metadata.TargetAudioFrequency;
                    Audio = new List <AudioStream> {
                        audioStream
                    };
                }
                else if (IsVideo)
                {
                    VideoTranscoding        video    = (VideoTranscoding)TranscodingParameter;
                    TranscodedVideoMetadata metadata = MediaConverter.GetTranscodedVideoMetadata(video);
                    Metadata = new MetadataStream
                    {
                        Mime = info.Metadata[edition].Mime,
                        VideoContainerType = metadata.TargetVideoContainer,
                        Bitrate            = metadata.TargetAudioBitrate > 0 && metadata.TargetVideoBitrate > 0 ? metadata.TargetAudioBitrate + metadata.TargetVideoBitrate : null,
                        Duration           = info.Metadata[edition].Duration,
                        Size = Client.EstimateTransodedSize ? (metadata.TargetAudioBitrate > 0 && info.Metadata[edition].Duration > 0 ? Convert.ToInt64((metadata.TargetAudioBitrate * 1024 * info.Metadata[edition].Duration) / 8.0) : (long?)null) : null,
                    };
                    Video = new VideoStream
                    {
                        AspectRatio      = metadata.TargetVideoAspectRatio,
                        Bitrate          = metadata.TargetVideoBitrate,
                        Codec            = metadata.TargetVideoCodec,
                        Framerate        = metadata.TargetVideoFrameRate,
                        HeaderLevel      = metadata.TargetLevel,
                        ProfileType      = metadata.TargetProfile,
                        RefLevel         = metadata.TargetLevel,
                        Height           = metadata.TargetVideoMaxHeight,
                        PixelAspectRatio = metadata.TargetVideoPixelAspectRatio,
                        PixelFormatType  = metadata.TargetVideoPixelFormat,
                        TimestampType    = metadata.TargetVideoTimestamp,
                        Width            = metadata.TargetVideoMaxWidth,
                    };
                    AudioStream audioStream = new AudioStream();
                    audioStream.Bitrate   = metadata.TargetAudioBitrate;
                    audioStream.Channels  = metadata.TargetAudioChannels;
                    audioStream.Codec     = metadata.TargetAudioCodec;
                    audioStream.Frequency = metadata.TargetAudioFrequency;
                    Audio = new List <AudioStream> {
                        audioStream
                    };
                }
            }

            if (IsImage)
            {
                profileList = DlnaProfiles.ResolveImageProfile(Metadata.ImageContainerType, Image.Width, Image.Height);
            }
            else if (IsAudio)
            {
                var audio = Audio.FirstOrDefault();
                profileList = DlnaProfiles.ResolveAudioProfile(Metadata.AudioContainerType, audio?.Codec ?? AudioCodec.Unknown, audio?.Bitrate, audio?.Frequency, audio?.Channels);
            }
            else if (IsVideo)
            {
                var audio = Audio.FirstOrDefault();
                profileList = DlnaProfiles.ResolveVideoProfile(Metadata.VideoContainerType, Video.Codec, audio?.Codec ?? AudioCodec.Unknown, Video.ProfileType, Video.HeaderLevel,
                                                               Video.Framerate, Video.Width, Video.Height, Video.Bitrate, audio?.Bitrate, Video.TimestampType);
            }

            string profile = "";
            string mime    = info.Metadata[edition].Mime;

            if (DlnaProfiles.TryFindCompatibleProfile(Client, profileList, ref profile, ref mime))
            {
                DlnaMime    = mime;
                DlnaProfile = profile;
            }
        }
Ejemplo n.º 16
0
        public async Task Initialize(MediaItem item, int?edition = null)
        {
            IsLive      = false;
            MediaItemId = item.MediaItemId;
            if (MediaItemAspect.TryGetAttribute(item.Aspects, MediaAspect.ATTR_TITLE, out string title))
            {
                MediaItemTitle = title;
            }
            else
            {
                MediaItemTitle = "?";
            }

            var info = await MediaAnalyzer.ParseMediaItemAsync(item, edition);

            if (info == null)
            {
                Logger.Warn("MediaServer: Mediaitem {0} couldn't be analyzed", item.MediaItemId);
                return;
            }

            if (item.Aspects.ContainsKey(AudioAspect.ASPECT_ID))
            {
                IsAudio = true;
            }
            else if (item.Aspects.ContainsKey(ImageAspect.ASPECT_ID))
            {
                IsImage = true;
            }
            else if (item.Aspects.ContainsKey(VideoAspect.ASPECT_ID))
            {
                IsVideo = true;
            }
            else
            {
                Logger.Warn("MediaServer: Mediaitem {0} contains no required aspect information", item.MediaItemId);
                return;
            }

            if (item.HasEditions && !edition.HasValue)
            {
                //Find edition with best matching audio that doesn't require transcoding
                int  currentPriority            = -1;
                bool currentRequiresTranscoding = false;
                var  preferredAudioLang         = Client.PreferredAudioLanguages.ToList();
                foreach (var checkEdition in info.Metadata.Keys)
                {
                    for (int idx = 0; idx < info.Audio[checkEdition].Count; idx++)
                    {
                        VideoTranscoding video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                             info, checkEdition, Client.PreferredAudioLanguages, IsLive, "");

                        bool requiresTranscoding = video != null;

                        for (int priority = 0; priority < preferredAudioLang.Count; priority++)
                        {
                            if (preferredAudioLang[priority].Equals(info.Audio[checkEdition][idx].Language, StringComparison.InvariantCultureIgnoreCase) == true)
                            {
                                if (currentPriority == -1 || priority < currentPriority || (!requiresTranscoding && currentRequiresTranscoding && priority == currentPriority))
                                {
                                    currentPriority            = priority;
                                    currentRequiresTranscoding = requiresTranscoding;
                                    edition = checkEdition;
                                }
                            }
                        }
                    }
                }
            }
            if (!edition.HasValue)
            {
                //Assign first edition
                _edition = info.Metadata.Min(m => m.Key);
            }
            else
            {
                _edition = edition.Value;
            }

            string transcodeId = item.MediaItemId.ToString() + "_" + Client.Profile.ID;

            if (MediaServerPlugin.Settings.TranscodingAllowed)
            {
                if (IsAudio)
                {
                    AudioTranscoding audio = TranscodeProfileManager.GetAudioTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                         info, _edition, IsLive, transcodeId);
                    TranscodingParameter = audio;
                }
                else if (IsImage)
                {
                    ImageTranscoding image = TranscodeProfileManager.GetImageTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                         info, _edition, transcodeId);
                    TranscodingParameter = image;
                }
                else if (IsVideo)
                {
                    VideoTranscoding video = TranscodeProfileManager.GetVideoTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                         info, _edition, Client.PreferredAudioLanguages, IsLive, transcodeId);
                    if (video != null)
                    {
                        if (video.TargetVideoContainer == VideoContainer.Hls)
                        {
                            IsSegmented = true;
                        }
                        if (MediaServerPlugin.Settings.HardcodedSubtitlesAllowed == false)
                        {
                            video.TargetSubtitleSupport = SubtitleSupport.None;
                        }
                    }
                    TranscodingParameter = video;
                }
            }

            if (TranscodingParameter == null)
            {
                if (IsVideo)
                {
                    VideoTranscoding video = TranscodeProfileManager.GetVideoSubtitleTranscoding(ProfileManager.TRANSCODE_PROFILE_SECTION, Client.Profile.ID,
                                                                                                 info, _edition, IsLive, transcodeId);
                    if (video != null)
                    {
                        if (video.TargetVideoContainer == VideoContainer.Hls)
                        {
                            IsSegmented = true;
                        }
                        if (MediaServerPlugin.Settings.HardcodedSubtitlesAllowed == false)
                        {
                            video.TargetSubtitleSupport = SubtitleSupport.None;
                        }
                    }
                    SubtitleTranscodingParameter = video;
                }
            }

            //Assign some extra meta data
            DateTime?aspectDate;

            if (MediaItemAspect.TryGetAttribute(item.Aspects, ImporterAspect.ATTR_DATEADDED, out aspectDate))
            {
                MediaItemAddedDate = aspectDate;
            }
            if (MediaItemAspect.TryGetAttribute(item.Aspects, MediaAspect.ATTR_RECORDINGTIME, out aspectDate))
            {
                MediaItemRecordingDate = aspectDate;
            }

            if (IsVideo)
            {
                if (MediaItemAspect.TryGetAspects(item.Aspects, VideoStreamAspect.Metadata, out var videoStreamAspects))
                {
                    MediaItemWidth  = videoStreamAspects.FirstOrDefault()?.GetAttributeValue <int?>(VideoStreamAspect.ATTR_WIDTH);
                    MediaItemHeight = videoStreamAspects.FirstOrDefault()?.GetAttributeValue <int?>(VideoStreamAspect.ATTR_HEIGHT);
                }
            }
            else if (IsImage)
            {
                if (MediaItemAspect.TryGetAspect(item.Aspects, ImageAspect.Metadata, out var imageAspect))
                {
                    MediaItemImageMake        = imageAspect?.GetAttributeValue <string>(ImageAspect.ATTR_MAKE);
                    MediaItemImageMake        = imageAspect?.GetAttributeValue <string>(ImageAspect.ATTR_MODEL);
                    MediaItemImageMake        = imageAspect?.GetAttributeValue <string>(ImageAspect.ATTR_FNUMBER);
                    MediaItemImageMake        = imageAspect?.GetAttributeValue <string>(ImageAspect.ATTR_ISO_SPEED);
                    MediaItemImageMake        = imageAspect?.GetAttributeValue <string>(ImageAspect.ATTR_EXPOSURE_TIME);
                    MediaItemImageOrientation = imageAspect?.GetAttributeValue <int?>(ImageAspect.ATTR_ORIENTATION);
                    MediaItemWidth            = imageAspect?.GetAttributeValue <int?>(ImageAspect.ATTR_WIDTH);
                    MediaItemHeight           = imageAspect?.GetAttributeValue <int?>(ImageAspect.ATTR_HEIGHT);
                }
            }

            AssignDlnaMetadata(info, _edition);
        }
 protected override Task <bool> ExtractSubtitleFileAsync(int sourceMediaIndex, VideoTranscoding video, SubtitleStream subtitle, string subtitleEncoding, string targetFilePath, double timeStart)
 {
     return(_ffMpegCommandline.ExtractSubtitleFileAsync(sourceMediaIndex, video, subtitle, subtitleEncoding, targetFilePath, timeStart));
 }