public async Task <ActionResult> OnPlaybackStart( [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] string?mediaSourceId, [FromQuery] int?audioStreamIndex, [FromQuery] int?subtitleStreamIndex, [FromQuery] PlayMethod?playMethod, [FromQuery] string?liveStreamId, [FromQuery] string?playSessionId, [FromQuery] bool canSeek = false) { var playbackStartInfo = new PlaybackStartInfo { CanSeek = canSeek, ItemId = itemId, MediaSourceId = mediaSourceId, AudioStreamIndex = audioStreamIndex, SubtitleStreamIndex = subtitleStreamIndex, PlayMethod = playMethod ?? PlayMethod.Transcode, PlaySessionId = playSessionId, LiveStreamId = liveStreamId }; playbackStartInfo.PlayMethod = ValidatePlayMethod(playbackStartInfo.PlayMethod, playbackStartInfo.PlaySessionId); playbackStartInfo.SessionId = RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id; await _sessionManager.OnPlaybackStart(playbackStartInfo).ConfigureAwait(false); return(NoContent()); }
public async Task <ActionResult> OnPlaybackProgress( [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] string?mediaSourceId, [FromQuery] long?positionTicks, [FromQuery] int?audioStreamIndex, [FromQuery] int?subtitleStreamIndex, [FromQuery] int?volumeLevel, [FromQuery] PlayMethod?playMethod, [FromQuery] string?liveStreamId, [FromQuery] string?playSessionId, [FromQuery] RepeatMode?repeatMode, [FromQuery] bool isPaused = false, [FromQuery] bool isMuted = false) { var playbackProgressInfo = new PlaybackProgressInfo { ItemId = itemId, PositionTicks = positionTicks, IsMuted = isMuted, IsPaused = isPaused, MediaSourceId = mediaSourceId, AudioStreamIndex = audioStreamIndex, SubtitleStreamIndex = subtitleStreamIndex, VolumeLevel = volumeLevel, PlayMethod = playMethod ?? PlayMethod.Transcode, PlaySessionId = playSessionId, LiveStreamId = liveStreamId, RepeatMode = repeatMode ?? RepeatMode.RepeatNone }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return(NoContent()); }
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Video, MediaSource = item, RunTimeTicks = item.RunTimeTicks, Context = options.Context, DeviceProfile = options.Profile }; playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles); MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex); int? audioStreamIndex = null; if (audioStream != null) { audioStreamIndex = audioStream.Index; } MediaStream videoStream = item.VideoStream; // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay); bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream); _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path", isEligibleForDirectPlay, isEligibleForDirectStream); if (isEligibleForDirectPlay || isEligibleForDirectStream) { // See if it can be direct played PlayMethod?directPlay = GetVideoDirectPlayProfile(options.Profile, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); if (directPlay != null) { playlistItem.PlayMethod = directPlay.Value; playlistItem.Container = item.Container; if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; } return(playlistItem); } } // Can't direct play, find the transcoding profile TranscodingProfile transcodingProfile = null; foreach (TranscodingProfile i in options.Profile.TranscodingProfiles) { if (i.Type == playlistItem.MediaType && i.Context == options.Context) { transcodingProfile = i; break; } } if (transcodingProfile != null) { if (!item.SupportsTranscoding) { return(null); } if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; } playlistItem.PlayMethod = PlayMethod.Transcode; playlistItem.Container = transcodingProfile.Container; playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(','); playlistItem.VideoCodec = transcodingProfile.VideoCodec; playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps; playlistItem.ForceLiveStream = transcodingProfile.ForceLiveStream; if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)) { int transcodingMaxAudioChannels; if (IntHelper.TryParseCultureInvariant(transcodingProfile.MaxAudioChannels, out transcodingMaxAudioChannels)) { playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels; } } playlistItem.SubProtocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; List <ProfileCondition> videoTranscodingConditions = new List <ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec, transcodingProfile.Container)) { foreach (ProfileCondition c in i.Conditions) { videoTranscodingConditions.Add(c); } break; } } ApplyTranscodingConditions(playlistItem, videoTranscodingConditions); List <ProfileCondition> audioTranscodingConditions = new List <ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.VideoAudio && i.ContainsCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container)) { foreach (ProfileCondition c in i.Conditions) { audioTranscodingConditions.Add(c); } break; } } ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); // Honor requested max channels if (options.MaxAudioChannels.HasValue) { int currentValue = playlistItem.MaxAudioChannels ?? options.MaxAudioChannels.Value; playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); } int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream); playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); int?maxBitrateSetting = options.GetMaxBitrate(); // Honor max rate if (maxBitrateSetting.HasValue) { int videoBitrate = maxBitrateSetting.Value; if (playlistItem.AudioBitrate.HasValue) { videoBitrate -= playlistItem.AudioBitrate.Value; } // Make sure the video bitrate is lower than bitrate settings but at least 64k int currentValue = playlistItem.VideoBitrate ?? videoBitrate; playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000); } } return(playlistItem); }
private StreamInfo?BuildVideoItemSimpleTest(VideoOptions options, PlayMethod?playMethod, TranscodeReason why, string transcodeMode, string transcodeProtocol) { if (string.IsNullOrEmpty(transcodeProtocol)) { transcodeProtocol = playMethod == PlayMethod.DirectStream ? "http" : "HLS.ts"; } var builder = GetStreamBuilder(); var val = builder.BuildVideoItem(options); Assert.NotNull(val); if (playMethod != null) { Assert.Equal(playMethod, val.PlayMethod); } Assert.Equal(why, val.TranscodeReasons); var audioStreamIndexInput = options.AudioStreamIndex; var targetVideoStream = val.TargetVideoStream; var targetAudioStream = val.TargetAudioStream; var mediaSource = options.MediaSources.First(source => source.Id == val.MediaSourceId); Assert.NotNull(mediaSource); var videoStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Video); var audioStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio); // TODO: Check AudioStreamIndex vs options.AudioStreamIndex var inputAudioStream = mediaSource.GetDefaultAudioStream(audioStreamIndexInput ?? mediaSource.DefaultAudioStreamIndex); var uri = ParseUri(val); if (playMethod == PlayMethod.DirectPlay) { // Check expected container var containers = ContainerProfile.SplitValue(mediaSource.Container); // TODO: Test transcode too // Assert.Contains(uri.Extension, containers); // Check expected video codec (1) Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec); // Check expected audio codecs (1) Assert.Contains(targetAudioStream.Codec, val.TargetAudioCodec); Assert.Single(val.TargetAudioCodec); // Assert.Single(val.AudioCodecs); if (transcodeMode == "DirectStream") { Assert.Equal(val.Container, uri.Extension); } } else if (playMethod == PlayMethod.DirectStream || playMethod == PlayMethod.Transcode) { Assert.NotNull(val.Container); Assert.NotEmpty(val.VideoCodecs); Assert.NotEmpty(val.AudioCodecs); // Check expected container (todo: this could be a test param) if (transcodeProtocol == "http") { // Assert.Equal("webm", val.Container); Assert.Equal(val.Container, uri.Extension); Assert.Equal("stream", uri.Filename); Assert.Equal("http", val.SubProtocol); } else if (transcodeProtocol == "HLS.mp4") { Assert.Equal("mp4", val.Container); Assert.Equal("m3u8", uri.Extension); Assert.Equal("master", uri.Filename); Assert.Equal("hls", val.SubProtocol); } else { Assert.Equal("ts", val.Container); Assert.Equal("m3u8", uri.Extension); Assert.Equal("master", uri.Filename); Assert.Equal("hls", val.SubProtocol); } // Full transcode if (transcodeMode == "Transcode") { if ((val.TranscodeReasons & (StreamBuilder.ContainerReasons | TranscodeReason.DirectPlayError)) == 0) { Assert.All( videoStreams, stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs)); } // TODO: Fill out tests here } // DirectStream and Remux else { // Check expected video codec (1) Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec); if (transcodeMode == "DirectStream") { // Check expected audio codecs (1) if (!targetAudioStream.IsExternal) { if (val.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported)) { Assert.Contains(targetAudioStream.Codec, val.AudioCodecs); } else { Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs); } } } else if (transcodeMode == "Remux") { // Check expected audio codecs (1) Assert.Contains(targetAudioStream.Codec, val.AudioCodecs); Assert.Single(val.AudioCodecs); } // Video details var videoStream = targetVideoStream; Assert.False(val.EstimateContentLength); Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo); Assert.Contains(videoStream.Profile?.ToLowerInvariant() ?? string.Empty, val.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? Array.Empty <string>()); Assert.Equal(videoStream.Level, val.TargetVideoLevel); Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth); Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue); // Audio codec not supported if ((why & TranscodeReason.AudioCodecNotSupported) != 0) { // Audio stream specified if (options.AudioStreamIndex >= 0) { // TODO:fixme if (!targetAudioStream.IsExternal) { Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs); } } // Audio stream not specified else { // TODO: Fixme Assert.All(audioStreams, stream => { if (!stream.IsExternal) { Assert.DoesNotContain(stream.Codec, val.AudioCodecs); } }); } } } } else if (playMethod == null) { Assert.Null(val.SubProtocol); Assert.Equal("stream", uri.Filename); Assert.False(val.EstimateContentLength); Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo); } return(val); }
public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod?playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "") { var options = await GetVideoOptions(deviceName, mediaSource); var streamCount = options.MediaSources[0].MediaStreams.Count; if (streamCount > 0) { options.AudioStreamIndex = streamCount - 2; options.SubtitleStreamIndex = streamCount - 1; } var streamInfo = BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol); Assert.Equal(streamInfo?.AudioStreamIndex, options.AudioStreamIndex); Assert.Equal(streamInfo?.SubtitleStreamIndex, options.SubtitleStreamIndex); }
public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod?playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "") { var options = await GetVideoOptions(deviceName, mediaSource); BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol); }