/// <summary> /// Gets the name of the output audio codec /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> protected string GetAudioCodec(StreamRequest request) { var codec = request.AudioCodec; if (codec.HasValue) { if (codec == AudioCodecs.Aac) { return("libvo_aacenc"); } if (codec == AudioCodecs.Mp3) { return("libmp3lame"); } if (codec == AudioCodecs.Vorbis) { return("libvorbis"); } if (codec == AudioCodecs.Wma) { return("wmav2"); } return(codec.ToString().ToLower()); } return("copy"); }
/// <summary> /// Parses the dlna headers. /// </summary> /// <param name="request">The request.</param> private void ParseDlnaHeaders(StreamRequest request) { if (!request.StartTimeTicks.HasValue) { var timeSeek = GetHeader("TimeSeekRange.dlna.org"); request.StartTimeTicks = ParseTimeSeekHeader(timeSeek); } }
/// <summary> /// Parses query parameters as StreamOptions /// <summary> /// <param name="request">The stream request.</param> private void ParseStreamOptions(StreamRequest request) { foreach (var param in Request.QueryString) { if (char.IsLower(param.Key[0])) { // This was probably not parsed initially and should be a StreamOptions // TODO: This should be incorporated either in the lower framework for parsing requests // or the generated URL should correctly serialize it request.StreamOptions[param.Key] = param.Value; } } }
/// <summary> /// Gets the number of audio channels to specify on the command line /// </summary> /// <param name="request">The request.</param> /// <param name="audioStream">The audio stream.</param> /// <returns>System.Nullable{System.Int32}.</returns> protected int?GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream) { if (audioStream.Channels > 2 && request.AudioCodec.HasValue) { if (request.AudioCodec.Value == AudioCodecs.Wma) { // wmav2 currently only supports two channel output return(2); } } return(request.AudioChannels); }
/// <summary> /// Gets the slow seek command line parameter. /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> /// <value>The slow seek command line parameter.</value> protected string GetSlowSeekCommandLineParameter(StreamRequest request) { var time = request.StartTimeTicks; if (time.HasValue) { if (TimeSpan.FromTicks(time.Value).TotalSeconds - FastSeekOffsetSeconds > 0) { return(string.Format(" -ss {0}", FastSeekOffsetSeconds.ToString(UsCulture))); } } return(string.Empty); }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <returns>StreamState.</returns> protected StreamState GetState(StreamRequest request) { var item = DtoService.GetItemByDtoId(request.Id); var media = (IHasMediaStreams)item; var url = RequestContext.PathInfo; if (!request.AudioCodec.HasValue) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState { Item = item, Request = request, Url = url }; var videoRequest = request as VideoStreamRequest; if (videoRequest != null) { if (!videoRequest.VideoCodec.HasValue) { videoRequest.VideoCodec = InferVideoCodec(url); } state.VideoStream = GetMediaStream(media.MediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video); state.SubtitleStream = GetMediaStream(media.MediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false); state.AudioStream = GetMediaStream(media.MediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio); } else { state.AudioStream = GetMediaStream(media.MediaStreams, null, MediaStreamType.Audio, true); } return(state); }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task <StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { ParseDlnaHeaders(request); if (!string.IsNullOrWhiteSpace(request.Params)) { ParseParams(request); } ParseStreamOptions(request); var url = Request.PathInfo; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = EncodingHelper.InferAudioCodec(url); } var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) || string.Equals(GetHeader("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase); var state = new StreamState(MediaSourceManager, TranscodingJobType) { Request = request, RequestedUrl = url, UserAgent = Request.UserAgent, EnableDlnaHeaders = enableDlnaHeaders }; var auth = AuthorizationContext.GetAuthorizationInfo(Request); if (!auth.UserId.Equals(Guid.Empty)) { state.User = UserManager.GetUserById(auth.UserId); } //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) //{ // state.SegmentLength = 6; //} if (state.VideoRequest != null && !string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec)) { state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i)) ?? state.SupportedAudioCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(request.SubtitleCodec)) { state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToSubtitleCodec(i)) ?? state.SupportedSubtitleCodecs.FirstOrDefault(); } var item = LibraryManager.GetItemById(request.Id); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null); //if (primaryImage != null) //{ // state.AlbumCoverPath = primaryImage.Path; //} MediaSourceInfo mediaSource = null; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { var currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ? ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId) : null; if (currentJob != null) { mediaSource = currentJob.MediaSource; } if (mediaSource == null) { var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources[0] : mediaSources.Find(i => string.Equals(i.Id, request.MediaSourceId)); if (mediaSource == null && request.MediaSourceId.Equals(request.Id)) { mediaSource = mediaSources[0]; } } } else { var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false); mediaSource = liveStreamInfo.Item1; state.DirectStreamProvider = liveStreamInfo.Item2; } var videoRequest = request as VideoStreamRequest; EncodingHelper.AttachMediaSourceInfo(state, mediaSource, url); var container = Path.GetExtension(state.RequestedUrl); if (string.IsNullOrEmpty(container)) { container = request.Container; } if (string.IsNullOrEmpty(container)) { container = request.Static ? StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio) : GetOutputFileExtension(state); } state.OutputContainer = (container ?? string.Empty).TrimStart('.'); state.OutputAudioBitrate = EncodingHelper.GetAudioBitrateParam(state.Request, state.AudioStream); state.OutputAudioCodec = state.Request.AudioCodec; state.OutputAudioChannels = EncodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec); if (videoRequest != null) { state.OutputVideoCodec = state.VideoRequest.VideoCodec; state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); if (videoRequest != null) { EncodingHelper.TryStreamCopy(state); } if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var resolution = ResolutionNormalizer.Normalize( state.VideoStream?.BitRate, state.VideoStream?.Width, state.VideoStream?.Height, state.OutputVideoBitrate.Value, state.VideoStream?.Codec, state.OutputVideoCodec, videoRequest.MaxWidth, videoRequest.MaxHeight); videoRequest.MaxWidth = resolution.MaxWidth; videoRequest.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); var ext = string.IsNullOrWhiteSpace(state.OutputContainer) ? GetOutputFileExtension(state) : ('.' + state.OutputContainer); var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); state.OutputFilePath = GetOutputFilePath(state, encodingOptions, ext); return(state); }
/// <summary> /// Gets the slow seek command line parameter. /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> /// <value>The slow seek command line parameter.</value> protected string GetSlowSeekCommandLineParameter(StreamRequest request) { return string.Empty; }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task <StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { ParseDlnaHeaders(request); if (!string.IsNullOrWhiteSpace(request.Params)) { ParseParams(request); } var url = Request.PathInfo; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = EncodingHelper.InferAudioCodec(url); } var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType) { Request = request, RequestedUrl = url, UserAgent = Request.UserAgent }; var auth = AuthorizationContext.GetAuthorizationInfo(Request); if (!string.IsNullOrWhiteSpace(auth.UserId)) { state.User = UserManager.GetUserById(auth.UserId); } //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) //{ // state.SegmentLength = 6; //} if (state.VideoRequest != null) { if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec)) { state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); } } if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i)) ?? state.SupportedAudioCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(request.SubtitleCodec)) { state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToSubtitleCodec(i)) ?? state.SupportedSubtitleCodecs.FirstOrDefault(); } var item = LibraryManager.GetItemById(request.Id); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); MediaSourceInfo mediaSource = null; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ? ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId) : null; if (currentJob != null) { mediaSource = currentJob.MediaSource; } if (mediaSource == null) { var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)); if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase)) { mediaSource = mediaSources.First(); } } } else { var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false); mediaSource = liveStreamInfo.Item1; state.DirectStreamProvider = liveStreamInfo.Item2; } var videoRequest = request as VideoStreamRequest; EncodingHelper.AttachMediaSourceInfo(state, mediaSource, url); var container = Path.GetExtension(state.RequestedUrl); if (string.IsNullOrEmpty(container)) { container = request.Static ? state.InputContainer : (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.'); } state.OutputContainer = (container ?? string.Empty).TrimStart('.'); state.OutputAudioBitrate = EncodingHelper.GetAudioBitrateParam(state.Request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = state.Request.AudioCodec; state.OutputAudioChannels = EncodingHelper.GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec); if (videoRequest != null) { state.OutputVideoCodec = state.VideoRequest.VideoCodec; state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); if (videoRequest != null) { EncodingHelper.TryStreamCopy(state); } if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var resolution = ResolutionNormalizer.Normalize( state.VideoStream == null ? (int?)null : state.VideoStream.BitRate, state.OutputVideoBitrate.Value, state.VideoStream == null ? null : state.VideoStream.Codec, state.OutputVideoCodec, videoRequest.MaxWidth, videoRequest.MaxHeight); videoRequest.MaxWidth = resolution.MaxWidth; videoRequest.MaxHeight = resolution.MaxHeight; } ApplyDeviceProfileSettings(state); } else { ApplyDeviceProfileSettings(state); } state.OutputFilePath = GetOutputFilePath(state); return(state); }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { if (!string.IsNullOrWhiteSpace(request.Params)) { ParseParams(request); } if (request.ThrowDebugError) { throw new InvalidOperationException("You asked for a debug error, you got one."); } var user = AuthorizationRequestFilterAttribute.GetCurrentUser(Request, UserManager); var url = Request.PathInfo; if (!request.AudioCodec.HasValue) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState { Request = request, RequestedUrl = url }; var item = DtoService.GetItemByDtoId(request.Id); if (user != null && item.GetPlayAccess(user) != PlayAccess.Full) { throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); } if (item is ILiveTvRecording) { var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.PlayableStreamFileNames = new List<string>(); if (!string.IsNullOrEmpty(recording.RecordingInfo.Path) && File.Exists(recording.RecordingInfo.Path)) { state.MediaPath = recording.RecordingInfo.Path; state.IsRemote = false; } else if (!string.IsNullOrEmpty(recording.RecordingInfo.Url)) { state.MediaPath = recording.RecordingInfo.Url; state.IsRemote = true; } else { var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false); state.LiveTvStreamId = streamInfo.Id; if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path)) { state.MediaPath = streamInfo.Path; state.IsRemote = false; } else if (!string.IsNullOrEmpty(streamInfo.Url)) { state.MediaPath = streamInfo.Url; state.IsRemote = true; } } //state.RunTimeTicks = recording.RunTimeTicks; state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress; state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress; state.AudioSync = "1000"; state.DeInterlace = true; } else if (item is LiveTvChannel) { var channel = LiveTvManager.GetInternalChannel(request.Id); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.PlayableStreamFileNames = new List<string>(); var streamInfo = await LiveTvManager.GetChannelStream(request.Id, cancellationToken).ConfigureAwait(false); state.LiveTvStreamId = streamInfo.Id; if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path)) { state.MediaPath = streamInfo.Path; state.IsRemote = false; } else if (!string.IsNullOrEmpty(streamInfo.Url)) { state.MediaPath = streamInfo.Url; state.IsRemote = true; } state.SendInputOverStandardInput = true; state.ReadInputAtNativeFramerate = true; state.AudioSync = "1000"; state.DeInterlace = true; } else { state.MediaPath = item.Path; state.IsRemote = item.LocationType == LocationType.Remote; var video = item as Video; if (video != null) { state.IsInputVideo = true; state.VideoType = video.VideoType; state.IsoType = video.IsoType; state.PlayableStreamFileNames = video.PlayableStreamFileNames == null ? new List<string>() : video.PlayableStreamFileNames.ToList(); } state.RunTimeTicks = item.RunTimeTicks; } var videoRequest = request as VideoStreamRequest; var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(); if (videoRequest != null) { if (!videoRequest.VideoCodec.HasValue) { videoRequest.VideoCodec = InferVideoCodec(url); } state.VideoStream = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video); state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false); state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio); EnforceResolutionLimit(state, videoRequest); } else { state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } state.HasMediaStreams = mediaStreams.Count > 0; state.SegmentLength = state.ReadInputAtNativeFramerate ? 3 : 10; state.HlsListSize = state.ReadInputAtNativeFramerate ? 20 : 1440; return state; }
/// <summary> /// Gets the fast seek command line parameter. /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> /// <value>The fast seek command line parameter.</value> protected string GetFastSeekCommandLineParameter(StreamRequest request) { var time = request.StartTimeTicks; if (time.HasValue && time.Value > 0) { return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time.Value)); } return string.Empty; }
private int? GetAudioBitrateParam(StreamRequest request, MediaStream audioStream) { if (request.AudioBitRate.HasValue) { // Make sure we don't request a bitrate higher than the source var currentBitrate = audioStream == null ? request.AudioBitRate.Value : audioStream.BitRate ?? request.AudioBitRate.Value; return request.AudioBitRate.Value; //return Math.Min(currentBitrate, request.AudioBitRate.Value); } return null; }
/// <summary> /// Parses the parameters. /// </summary> /// <param name="request">The request.</param> private void ParseParams(StreamRequest request) { var vals = request.Params.Split(';'); var videoRequest = request as VideoStreamRequest; for (var i = 0; i < vals.Length; i++) { var val = vals[i]; if (string.IsNullOrWhiteSpace(val)) { continue; } switch (i) { case 0: request.DeviceProfileId = val; break; case 1: request.DeviceId = val; break; case 2: request.MediaSourceId = val; break; case 3: request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); break; case 4: if (videoRequest != null) { videoRequest.VideoCodec = val; } break; case 5: request.AudioCodec = val; break; case 6: if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture); } break; case 7: if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture); } break; case 8: if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, CultureInfo.InvariantCulture); } break; case 9: request.AudioBitRate = int.Parse(val, CultureInfo.InvariantCulture); break; case 10: request.MaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture); break; case 11: if (videoRequest != null) { videoRequest.MaxFramerate = float.Parse(val, CultureInfo.InvariantCulture); } break; case 12: if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, CultureInfo.InvariantCulture); } break; case 13: if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, CultureInfo.InvariantCulture); } break; case 14: request.StartTimeTicks = long.Parse(val, CultureInfo.InvariantCulture); break; case 15: if (videoRequest != null) { videoRequest.Level = val; } break; case 16: if (videoRequest != null) { videoRequest.MaxRefFrames = int.Parse(val, CultureInfo.InvariantCulture); } break; case 17: if (videoRequest != null) { videoRequest.MaxVideoBitDepth = int.Parse(val, CultureInfo.InvariantCulture); } break; case 18: if (videoRequest != null) { videoRequest.Profile = val; } break; case 19: // cabac no longer used break; case 20: request.PlaySessionId = val; break; case 21: // api_key break; case 22: request.LiveStreamId = val; break; case 23: // Duplicating ItemId because of MediaMonkey break; case 24: if (videoRequest != null) { videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } break; case 25: if (!string.IsNullOrWhiteSpace(val) && videoRequest != null) { if (Enum.TryParse(val, out SubtitleDeliveryMethod method)) { videoRequest.SubtitleMethod = method; } } break; case 26: request.TranscodingMaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture); break; case 27: if (videoRequest != null) { videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } break; case 28: request.Tag = val; break; case 29: if (videoRequest != null) { videoRequest.RequireAvc = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } break; case 30: request.SubtitleCodec = val; break; case 31: if (videoRequest != null) { videoRequest.RequireNonAnamorphic = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } break; case 32: if (videoRequest != null) { videoRequest.DeInterlace = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } break; case 33: request.TranscodeReasons = val; break; } } }
/// <summary> /// Parses the parameters. /// </summary> /// <param name="request">The request.</param> private void ParseParams(StreamRequest request) { var vals = request.Params.Split(';'); var videoRequest = request as VideoStreamRequest; for (var i = 0; i < vals.Length; i++) { var val = vals[i]; if (string.IsNullOrWhiteSpace(val)) { continue; } if (i == 0) { request.DeviceProfileId = val; } else if (i == 1) { request.DeviceId = val; } else if (i == 2) { request.MediaSourceId = val; } else if (i == 3) { request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } else if (i == 4) { if (videoRequest != null) { videoRequest.VideoCodec = val; } } else if (i == 5) { request.AudioCodec = val; } else if (i == 6) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); } } else if (i == 7) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); } } else if (i == 8) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, UsCulture); } } else if (i == 9) { request.AudioBitRate = int.Parse(val, UsCulture); } else if (i == 10) { request.MaxAudioChannels = int.Parse(val, UsCulture); } else if (i == 11) { if (videoRequest != null) { videoRequest.MaxFramerate = float.Parse(val, UsCulture); } } else if (i == 12) { if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, UsCulture); } } else if (i == 13) { if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, UsCulture); } } else if (i == 14) { request.StartTimeTicks = long.Parse(val, UsCulture); } else if (i == 15) { if (videoRequest != null) { videoRequest.Level = val; } } else if (i == 16) { if (videoRequest != null) { videoRequest.MaxRefFrames = int.Parse(val, UsCulture); } } else if (i == 17) { if (videoRequest != null) { videoRequest.MaxVideoBitDepth = int.Parse(val, UsCulture); } } else if (i == 18) { if (videoRequest != null) { videoRequest.Profile = val; } } else if (i == 19) { // cabac no longer used } else if (i == 20) { request.PlaySessionId = val; } else if (i == 21) { // api_key } else if (i == 22) { request.LiveStreamId = val; } else if (i == 23) { // Duplicating ItemId because of MediaMonkey } else if (i == 24) { if (videoRequest != null) { videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 25) { if (videoRequest != null) { videoRequest.ForceLiveStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 26) { if (!string.IsNullOrWhiteSpace(val) && videoRequest != null) { SubtitleDeliveryMethod method; if (Enum.TryParse(val, out method)) { videoRequest.SubtitleMethod = method; } } } else if (i == 27) { request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture); } else if (i == 28) { if (videoRequest != null) { videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } } }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <returns>StreamState.</returns> protected StreamState GetState(StreamRequest request) { var item = DtoService.GetItemByDtoId(request.Id); var media = (IHasMediaStreams)item; var url = RequestContext.PathInfo; if (!request.AudioCodec.HasValue) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState { Item = item, Request = request, Url = url }; var videoRequest = request as VideoStreamRequest; if (videoRequest != null) { if (!videoRequest.VideoCodec.HasValue) { videoRequest.VideoCodec = InferVideoCodec(url); } state.VideoStream = GetMediaStream(media.MediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video); state.SubtitleStream = GetMediaStream(media.MediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false); state.AudioStream = GetMediaStream(media.MediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio); } else { state.AudioStream = GetMediaStream(media.MediaStreams, null, MediaStreamType.Audio, true); } return state; }
/// <summary> /// Gets the name of the output audio codec /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> protected string GetAudioCodec(StreamRequest request) { var codec = request.AudioCodec; if (codec.HasValue) { if (codec == AudioCodecs.Aac) { return "libvo_aacenc"; } if (codec == AudioCodecs.Mp3) { return "libmp3lame"; } if (codec == AudioCodecs.Vorbis) { return "libvorbis"; } if (codec == AudioCodecs.Wma) { return "wmav2"; } return codec.ToString().ToLower(); } return "copy"; }
/// <summary> /// Gets the number of audio channels to specify on the command line /// </summary> /// <param name="request">The request.</param> /// <param name="audioStream">The audio stream.</param> /// <returns>System.Nullable{System.Int32}.</returns> protected int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream) { if (audioStream != null) { if (audioStream.Channels > 2 && request.AudioCodec.HasValue) { if (request.AudioCodec.Value == AudioCodecs.Wma) { // wmav2 currently only supports two channel output return 2; } } } return request.AudioChannels; }
/// <summary> /// Gets the slow seek command line parameter. /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> /// <value>The slow seek command line parameter.</value> protected string GetSlowSeekCommandLineParameter(StreamRequest request) { var time = request.StartTimeTicks; if (time.HasValue) { if (TimeSpan.FromTicks(time.Value).TotalSeconds - FastSeekOffsetSeconds > 0) { return string.Format(" -ss {0}", FastSeekOffsetSeconds.ToString(UsCulture)); } } return string.Empty; }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { var url = Request.PathInfo; if (!request.AudioCodec.HasValue) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState { Request = request, RequestedUrl = url }; BaseItem item; if (string.Equals(request.Type, "Recording", StringComparison.OrdinalIgnoreCase)) { var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.PlayableStreamFileNames = new List<string>(); if (!string.IsNullOrEmpty(recording.RecordingInfo.Path) && File.Exists(recording.RecordingInfo.Path)) { state.MediaPath = recording.RecordingInfo.Path; state.IsRemote = false; } else if (!string.IsNullOrEmpty(recording.RecordingInfo.Url)) { state.MediaPath = recording.RecordingInfo.Url; state.IsRemote = true; } else { state.MediaPath = string.Format("http://localhost:{0}/mediabrowser/LiveTv/Recordings/{1}/Stream", ServerConfigurationManager.Configuration.HttpServerPortNumber, request.Id); state.IsRemote = true; } item = recording; } else { item = DtoService.GetItemByDtoId(request.Id); state.MediaPath = item.Path; state.IsRemote = item.LocationType == LocationType.Remote; var video = item as Video; if (video != null) { state.IsInputVideo = true; state.VideoType = video.VideoType; state.IsoType = video.IsoType; state.PlayableStreamFileNames = video.PlayableStreamFileNames == null ? new List<string>() : video.PlayableStreamFileNames.ToList(); } } var videoRequest = request as VideoStreamRequest; var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(); if (videoRequest != null) { if (!videoRequest.VideoCodec.HasValue) { videoRequest.VideoCodec = InferVideoCodec(url); } state.VideoStream = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video); state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false); state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio); } else { state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } state.HasMediaStreams = mediaStreams.Count > 0; return state; }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { ParseDlnaHeaders(request); if (!string.IsNullOrWhiteSpace(request.Params)) { ParseParams(request); } var url = Request.PathInfo; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState(MediaSourceManager, Logger) { Request = request, RequestedUrl = url }; //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) //{ // state.SegmentLength = 6; //} if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } var item = LibraryManager.GetItemById(request.Id); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); var archivable = item as IArchivable; state.IsInputArchive = archivable != null && archivable.IsArchive; MediaSourceInfo mediaSource; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)); if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase)) { mediaSource = mediaSources.First(); } } else { mediaSource = await MediaSourceManager.GetLiveStream(request.LiveStreamId, cancellationToken).ConfigureAwait(false); } var videoRequest = request as VideoStreamRequest; AttachMediaSourceInfo(state, mediaSource, videoRequest, url); var container = Path.GetExtension(state.RequestedUrl); if (string.IsNullOrEmpty(container)) { container = request.Static ? state.InputContainer : (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.'); } state.OutputContainer = (container ?? string.Empty).TrimStart('.'); state.OutputAudioBitrate = GetAudioBitrateParam(state.Request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = state.Request.AudioCodec; state.OutputAudioChannels = GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec); if (videoRequest != null) { state.OutputVideoCodec = state.VideoRequest.VideoCodec; state.OutputVideoBitrate = GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream); if (state.OutputVideoBitrate.HasValue) { var resolution = ResolutionNormalizer.Normalize( state.VideoStream == null ? (int?)null : state.VideoStream.BitRate, state.OutputVideoBitrate.Value, state.VideoStream == null ? null : state.VideoStream.Codec, state.OutputVideoCodec, videoRequest.MaxWidth, videoRequest.MaxHeight); videoRequest.MaxWidth = resolution.MaxWidth; videoRequest.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); if (videoRequest != null) { TryStreamCopy(state, videoRequest); } state.OutputFilePath = GetOutputFilePath(state); return state; }
/// <summary> /// Gets the number of audio channels to specify on the command line /// </summary> /// <param name="request">The request.</param> /// <param name="audioStream">The audio stream.</param> /// <param name="outputAudioCodec">The output audio codec.</param> /// <returns>System.Nullable{System.Int32}.</returns> private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream, string outputAudioCodec) { var inputChannels = audioStream == null ? null : audioStream.Channels; if (inputChannels <= 0) { inputChannels = null; } var codec = outputAudioCodec ?? string.Empty; if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) { // wmav2 currently only supports two channel output return Math.Min(2, inputChannels ?? 2); } if (request.MaxAudioChannels.HasValue) { var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1 ? 2 : 6; if (inputChannels.HasValue) { channelLimit = Math.Min(channelLimit, inputChannels.Value); } // If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels return Math.Min(request.MaxAudioChannels.Value, channelLimit); } return request.AudioChannels; }
/// <summary> /// Gets the number of audio channels to specify on the command line /// </summary> /// <param name="request">The request.</param> /// <param name="audioStream">The audio stream.</param> /// <param name="outputAudioCodec">The output audio codec.</param> /// <returns>System.Nullable{System.Int32}.</returns> private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream, string outputAudioCodec) { if (audioStream != null) { var codec = outputAudioCodec ?? string.Empty; if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) { // wmav2 currently only supports two channel output return 2; } } if (request.MaxAudioChannels.HasValue) { if (audioStream != null && audioStream.Channels.HasValue) { return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value); } return request.MaxAudioChannels.Value; } return request.AudioChannels; }
/// <summary> /// Parses the parameters. /// </summary> /// <param name="request">The request.</param> private void ParseParams(StreamRequest request) { var vals = request.Params.Split(';'); var videoRequest = request as VideoStreamRequest; for (var i = 0; i < vals.Length; i++) { var val = vals[i]; if (string.IsNullOrWhiteSpace(val)) { continue; } if (i == 0) { request.DeviceProfileId = val; } else if (i == 1) { request.DeviceId = val; } else if (i == 2) { request.MediaSourceId = val; } else if (i == 3) { request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } else if (i == 4) { if (videoRequest != null) { videoRequest.VideoCodec = val; } } else if (i == 5) { request.AudioCodec = val; } else if (i == 6) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); } } else if (i == 7) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); } } else if (i == 8) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, UsCulture); } } else if (i == 9) { request.AudioBitRate = int.Parse(val, UsCulture); } else if (i == 10) { request.MaxAudioChannels = int.Parse(val, UsCulture); } else if (i == 11) { if (videoRequest != null) { videoRequest.MaxFramerate = float.Parse(val, UsCulture); } } else if (i == 12) { if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, UsCulture); } } else if (i == 13) { if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, UsCulture); } } else if (i == 14) { request.StartTimeTicks = long.Parse(val, UsCulture); } else if (i == 15) { if (videoRequest != null) { videoRequest.Level = val; } } } }
private bool CanStreamCopyAudio(StreamRequest request, MediaStream audioStream) { // Source and target codecs must match if (string.IsNullOrEmpty(request.AudioCodec) || !string.Equals(request.AudioCodec, audioStream.Codec, StringComparison.OrdinalIgnoreCase)) { return false; } // Video bitrate must fall within requested value if (request.AudioBitRate.HasValue) { if (!audioStream.BitRate.HasValue || audioStream.BitRate.Value > request.AudioBitRate.Value) { return false; } } // Channels must fall within requested value var channels = request.AudioChannels ?? request.MaxAudioChannels; if (channels.HasValue) { if (!audioStream.Channels.HasValue || audioStream.Channels.Value > channels.Value) { return false; } } // Sample rate must fall within requested value if (request.AudioSampleRate.HasValue) { if (!audioStream.SampleRate.HasValue || audioStream.SampleRate.Value > request.AudioSampleRate.Value) { return false; } } return SupportsAutomaticVideoStreamCopy; }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { ParseDlnaHeaders(request); if (!string.IsNullOrWhiteSpace(request.Params)) { ParseParams(request); } var url = Request.PathInfo; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState(LiveTvManager, Logger) { Request = request, RequestedUrl = url }; if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } var item = LibraryManager.GetItemById(request.Id); List<MediaStream> mediaStreams = null; state.ItemType = item.GetType().Name; if (item is ILiveTvRecording) { var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); var path = recording.RecordingInfo.Path; var mediaUrl = recording.RecordingInfo.Url; var source = string.IsNullOrEmpty(request.MediaSourceId) ? recording.GetMediaSources(false).First() : recording.GetMediaSources(false).First(i => string.Equals(i.Id, request.MediaSourceId)); mediaStreams = source.MediaStreams; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; if (!string.IsNullOrEmpty(path)) { state.MediaPath = path; state.InputProtocol = MediaProtocol.File; } else if (!string.IsNullOrEmpty(mediaUrl)) { state.MediaPath = mediaUrl; state.InputProtocol = MediaProtocol.Http; } else { // No media info, so this is probably needed state.DeInterlace = true; } if (recording.RecordingInfo.Status == RecordingStatus.InProgress) { state.ReadInputAtNativeFramerate = true; } state.RunTimeTicks = recording.RunTimeTicks; state.OutputAudioSync = "1000"; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; state.InputContainer = recording.Container; } else if (item is LiveTvChannel) { var channel = LiveTvManager.GetInternalChannel(request.Id); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); mediaStreams = new List<MediaStream>(); state.ReadInputAtNativeFramerate = true; state.OutputAudioSync = "1000"; state.DeInterlace = true; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; } else if (item is IChannelMediaItem) { var mediaSource = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.InputProtocol = mediaSource.Protocol; state.MediaPath = mediaSource.Path; state.RunTimeTicks = item.RunTimeTicks; state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; mediaStreams = mediaSource.MediaStreams; } else { var hasMediaSources = (IHasMediaSources)item; var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? hasMediaSources.GetMediaSources(false).First() : hasMediaSources.GetMediaSources(false).First(i => string.Equals(i.Id, request.MediaSourceId)); mediaStreams = mediaSource.MediaStreams; state.MediaPath = mediaSource.Path; state.InputProtocol = mediaSource.Protocol; state.InputContainer = mediaSource.Container; if (item is Video) { state.IsInputVideo = true; if (mediaSource.VideoType.HasValue) { state.VideoType = mediaSource.VideoType.Value; } state.IsoType = mediaSource.IsoType; state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); if (mediaSource.Timestamp.HasValue) { state.InputTimestamp = mediaSource.Timestamp.Value; } } state.RunTimeTicks = mediaSource.RunTimeTicks; } // If it's a wtv and we don't have media info, we will probably need to deinterlace if (string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) && mediaStreams.Count == 0) { state.DeInterlace = true; } if (state.InputProtocol == MediaProtocol.Rtmp) { state.ReadInputAtNativeFramerate = true; } var videoRequest = request as VideoStreamRequest; AttachMediaStreamInfo(state, mediaStreams, videoRequest, url); state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 7; state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; var container = Path.GetExtension(state.RequestedUrl); if (string.IsNullOrEmpty(container)) { container = request.Static ? state.InputContainer : Path.GetExtension(GetOutputFilePath(state)); } state.OutputContainer = (container ?? string.Empty).TrimStart('.'); state.OutputAudioBitrate = GetAudioBitrateParam(state.Request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = GetAudioCodec(state.Request); state.OutputAudioChannels = GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec); if (videoRequest != null) { state.OutputVideoCodec = GetVideoCodec(videoRequest); state.OutputVideoBitrate = GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream); } ApplyDeviceProfileSettings(state); if (videoRequest != null) { if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) { state.OutputVideoCodec = "copy"; } if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs)) { state.OutputAudioCodec = "copy"; } } state.OutputFilePath = GetOutputFilePath(state); return state; }
/// <summary> /// Parses the parameters. /// </summary> /// <param name="request">The request.</param> private void ParseParams(StreamRequest request) { var vals = request.Params.Split(';'); var videoRequest = request as VideoStreamRequest; for (var i = 0; i < vals.Length; i++) { var val = vals[i]; if (string.IsNullOrWhiteSpace(val)) { continue; } if (i == 0) { request.DeviceProfileId = val; } else if (i == 1) { request.DeviceId = val; } else if (i == 2) { request.MediaSourceId = val; } else if (i == 3) { request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } else if (i == 4) { if (videoRequest != null) { videoRequest.VideoCodec = val; } } else if (i == 5) { request.AudioCodec = val; } else if (i == 6) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 7) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 8) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 9) { request.AudioBitRate = int.Parse(val, CultureInfo.InvariantCulture); } else if (i == 10) { request.MaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture); } else if (i == 11) { if (videoRequest != null) { videoRequest.MaxFramerate = float.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 12) { if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 13) { if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 14) { request.StartTimeTicks = long.Parse(val, CultureInfo.InvariantCulture); } else if (i == 15) { if (videoRequest != null) { videoRequest.Level = val; } } else if (i == 16) { if (videoRequest != null) { videoRequest.MaxRefFrames = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 17) { if (videoRequest != null) { videoRequest.MaxVideoBitDepth = int.Parse(val, CultureInfo.InvariantCulture); } } else if (i == 18) { if (videoRequest != null) { videoRequest.Profile = val; } } else if (i == 19) { // cabac no longer used } else if (i == 20) { request.PlaySessionId = val; } else if (i == 21) { // api_key } else if (i == 22) { request.LiveStreamId = val; } else if (i == 23) { // Duplicating ItemId because of MediaMonkey } else if (i == 24) { if (videoRequest != null) { videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 25) { if (!string.IsNullOrWhiteSpace(val) && videoRequest != null) { if (Enum.TryParse(val, out SubtitleDeliveryMethod method)) { videoRequest.SubtitleMethod = method; } } } else if (i == 26) { request.TranscodingMaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture); } else if (i == 27) { if (videoRequest != null) { videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 28) { request.Tag = val; } else if (i == 29) { if (videoRequest != null) { videoRequest.RequireAvc = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 30) { request.SubtitleCodec = val; } else if (i == 31) { if (videoRequest != null) { videoRequest.RequireNonAnamorphic = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 32) { if (videoRequest != null) { videoRequest.DeInterlace = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } else if (i == 33) { request.TranscodeReasons = val; } } }
private bool CanStreamCopyAudio(StreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs) { // Source and target codecs must match if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase)) { return false; } // Video bitrate must fall within requested value if (request.AudioBitRate.HasValue) { if (!audioStream.BitRate.HasValue || audioStream.BitRate.Value <= 0) { return false; } if (audioStream.BitRate.Value > request.AudioBitRate.Value) { return false; } } // Channels must fall within requested value var channels = request.AudioChannels ?? request.MaxAudioChannels; if (channels.HasValue) { if (!audioStream.Channels.HasValue || audioStream.Channels.Value <= 0) { return false; } if (audioStream.Channels.Value > channels.Value) { return false; } } // Sample rate must fall within requested value if (request.AudioSampleRate.HasValue) { if (!audioStream.SampleRate.HasValue || audioStream.SampleRate.Value <= 0) { return false; } if (audioStream.SampleRate.Value > request.AudioSampleRate.Value) { return false; } } return true; }
/// <summary> /// Parses the parameters. /// </summary> /// <param name="request">The request.</param> private void ParseParams(StreamRequest request) { var vals = request.Params.Split(';'); var videoRequest = request as VideoStreamRequest; for (var i = 0; i < vals.Length; i++) { var val = vals[i]; if (string.IsNullOrWhiteSpace(val)) { continue; } if (i == 0) { request.DeviceId = val; } else if (i == 1) { if (videoRequest != null) { videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true); } } else if (i == 2) { request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true); } else if (i == 3) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); } } else if (i == 4) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); } } else if (i == 5) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, UsCulture); } } else if (i == 6) { request.AudioBitRate = int.Parse(val, UsCulture); } else if (i == 7) { request.AudioChannels = int.Parse(val, UsCulture); } else if (i == 8) { if (videoRequest != null) { request.StartTimeTicks = long.Parse(val, UsCulture); } } else if (i == 9) { if (videoRequest != null) { videoRequest.Profile = val; } } else if (i == 10) { if (videoRequest != null) { videoRequest.Level = val; } } } }
/// <summary> /// Gets the name of the output audio codec /// </summary> /// <param name="request">The request.</param> /// <returns>System.String.</returns> private string GetAudioCodec(StreamRequest request) { var codec = request.AudioCodec; if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) { return "aac -strict experimental"; } if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) { return "libmp3lame"; } if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase)) { return "libvorbis"; } if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) { return "wmav2"; } return codec.ToLower(); }
/// <summary> /// Gets the state. /// </summary> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>StreamState.</returns> protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken) { ParseDlnaHeaders(request); if (!string.IsNullOrWhiteSpace(request.Params)) { ParseParams(request); } var user = AuthorizationRequestFilterAttribute.GetCurrentUser(Request, UserManager); var url = Request.PathInfo; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(url); } var state = new StreamState(LiveTvManager, Logger) { Request = request, RequestedUrl = url }; var item = string.IsNullOrEmpty(request.MediaSourceId) ? DtoService.GetItemByDtoId(request.Id) : DtoService.GetItemByDtoId(request.MediaSourceId); if (user != null && item.GetPlayAccess(user) != PlayAccess.Full) { throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); } if (item is ILiveTvRecording) { var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.PlayableStreamFileNames = new List<string>(); var path = recording.RecordingInfo.Path; var mediaUrl = recording.RecordingInfo.Url; if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl)) { var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false); state.LiveTvStreamId = streamInfo.Id; path = streamInfo.Path; mediaUrl = streamInfo.Url; } if (!string.IsNullOrEmpty(path)) { state.MediaPath = path; state.IsRemote = false; } else if (!string.IsNullOrEmpty(mediaUrl)) { state.MediaPath = mediaUrl; state.IsRemote = true; } state.RunTimeTicks = recording.RunTimeTicks; if (recording.RecordingInfo.Status == RecordingStatus.InProgress) { await Task.Delay(1000, cancellationToken).ConfigureAwait(false); } state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress; state.AudioSync = "1000"; state.DeInterlace = true; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; } else if (item is LiveTvChannel) { var channel = LiveTvManager.GetInternalChannel(request.Id); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.PlayableStreamFileNames = new List<string>(); var streamInfo = await LiveTvManager.GetChannelStream(request.Id, cancellationToken).ConfigureAwait(false); state.LiveTvStreamId = streamInfo.Id; if (!string.IsNullOrEmpty(streamInfo.Path)) { state.MediaPath = streamInfo.Path; state.IsRemote = false; await Task.Delay(1000, cancellationToken).ConfigureAwait(false); } else if (!string.IsNullOrEmpty(streamInfo.Url)) { state.MediaPath = streamInfo.Url; state.IsRemote = true; } state.ReadInputAtNativeFramerate = true; state.AudioSync = "1000"; state.DeInterlace = true; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; } else { state.MediaPath = item.Path; state.IsRemote = item.LocationType == LocationType.Remote; var video = item as Video; if (video != null) { state.IsInputVideo = true; state.VideoType = video.VideoType; state.IsoType = video.IsoType; state.PlayableStreamFileNames = video.PlayableStreamFileNames == null ? new List<string>() : video.PlayableStreamFileNames.ToList(); } state.RunTimeTicks = item.RunTimeTicks; } var videoRequest = request as VideoStreamRequest; var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(); if (videoRequest != null) { if (string.IsNullOrEmpty(videoRequest.VideoCodec)) { videoRequest.VideoCodec = InferVideoCodec(url); } state.VideoStream = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video); state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false); state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio); if (state.VideoStream != null && state.VideoStream.IsInterlaced) { state.DeInterlace = true; } EnforceResolutionLimit(state, videoRequest); } else { state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } state.HasMediaStreams = mediaStreams.Count > 0; state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10; state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; ApplyDeviceProfileSettings(state); if (videoRequest != null && state.VideoStream != null) { if (CanStreamCopyVideo(videoRequest, state.VideoStream, state.VideoType)) { videoRequest.VideoCodec = "copy"; } } if (state.AudioStream != null) { //if (CanStreamCopyAudio(request, state.AudioStream)) //{ // request.AudioCodec = "copy"; //} } return state; }