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); }
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); }
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); }
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); }
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); }
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); }
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); }
public static bool IsSquarePixelNeeded(VideoTranscoding video) { bool squarePixels = IsSquarePixel(video.SourceVideoStream.PixelAspectRatio); return((video.TargetVideoContainer == VideoContainer.Asf || video.TargetVideoContainer == VideoContainer.Flv) && squarePixels == false); }
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); }
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); }
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; } }
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)); }