public static async Task <WebBoolResult> ProcessAsync(IOwinContext context, string identifier) { bool result = true; if (identifier == null) { Logger.Debug("FinishStream: Identifier is null"); result = false; return(new WebBoolResult { Result = result }); } if (!StreamControl.ValidateIdentifier(identifier)) { Logger.Debug("FinishStream: Unknown identifier: {0}", identifier); result = false; return(new WebBoolResult { Result = result }); } // Remove the stream from the stream controller result = await StreamControl.DeleteStreamItemAsync(identifier); return(new WebBoolResult { Result = result }); }
public static async Task <WebStringResult> ProcessAsync(IOwinContext context, string identifier, string profileName, long startPosition) { if (identifier == null) { throw new BadRequestException("InitStream: identifier is null"); } if (profileName == null) { throw new BadRequestException("InitStream: profileName is null"); } StreamItem streamItem = await StreamControl.GetStreamItemAsync(identifier); if (streamItem == null) { throw new BadRequestException(string.Format("StartStream: Unknown identifier: {0}", identifier)); } // Prefer getting profile by name EndPointProfile profile = ProfileManager.Profiles.Values.FirstOrDefault(p => p.Name == profileName); // If no ptofile with the specified name, see if there's one with a matching id if (profile == null && !ProfileManager.Profiles.TryGetValue(profileName, out profile)) { throw new BadRequestException(string.Format("StartStream: Unknown profile: {0}", profileName)); } streamItem.Profile = profile; // Seeking is not supported in live streams streamItem.StartPosition = streamItem.RequestedMediaItem is LiveTvMediaItem ? 0 : startPosition; Guid?userId = ResourceAccessUtils.GetUser(context); streamItem.TranscoderObject = new ProfileMediaItem(identifier, profile, streamItem.IsLive); await streamItem.TranscoderObject.Initialize(userId, streamItem.RequestedMediaItem, null); if (streamItem.TranscoderObject.TranscodingParameter is VideoTranscoding vt) { vt.HlsBaseUrl = string.Format("RetrieveStream?identifier={0}&hls=", identifier); } await StreamControl.StartStreamingAsync(identifier, startPosition); string filePostFix = "&file=media.ts"; if (profile.MediaTranscoding?.VideoTargets?.Any(t => t.Target.VideoContainerType == VideoContainer.Hls) ?? false) { filePostFix = "&file=manifest.m3u8"; //Must be added for some clients to work (Android mostly) } string url = GetBaseStreamUrl.GetBaseStreamURL(context) + "/MPExtended/StreamingService/stream/RetrieveStream?identifier=" + identifier + filePostFix; return(new WebStringResult { Result = url }); }
private void StartUp() { Logger.Debug("MP2Extended: Registering HTTP resource access module"); ServiceRegistration.Get <IResourceServer>().AddHttpModule((typeof(WebResourceAccessModule))); if (Settings.OnlineVideosEnabled) { OnlineVideosManager = new OnlineVideosManager(); // must be loaded after the settings are loaded } StreamControl.StartStreamCleanupTask(); }
public static Task <IList <WebStreamingSession> > ProcessAsync(IOwinContext context, string filter = null) { return(Task.FromResult <IList <WebStreamingSession> >(StreamControl.GetStreamItems().Select(streamItem => new WebStreamingSession { ClientDescription = streamItem.Value.ClientDescription, Profile = streamItem.Value.Profile.Name, Identifier = streamItem.Key, StartPosition = streamItem.Value.StartPosition, TranscodingInfo = new WebTranscodingInfo(streamItem.Value.StreamContext as TranscodeContext), StartTime = streamItem.Value.StartTimeUtc.ToLocalTime(), SourceId = streamItem.Value.RequestedMediaItem.MediaItemId.ToString(), ClientIPAddress = streamItem.Value.ClientIp, DisplayName = streamItem.Value.Title, }).ToList())); }
public void Init() { byte[] buffer = new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; MemoryStream ms = new MemoryStream(buffer); byte[] buffer2 = new byte[10] { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; Stream ms2 = new MemoryStream(buffer); streamControl = new StreamControl(2, 5); streamControl.AddStream("one", ms); streamControl.AddStream("two", ms2); }
public static async Task <WebTranscodingInfo> ProcessAsync(IOwinContext context, string identifier, long?playerPosition) { if (identifier == null) { throw new BadRequestException("GetTranscodingInfo: identifier is null"); } StreamItem streamItem = await StreamControl.GetStreamItemAsync(identifier); if (streamItem == null) { throw new BadRequestException(string.Format("GetTranscodingInfo: Unknown identifier: {0}", identifier)); } return(new WebTranscodingInfo(streamItem.StreamContext as TranscodeContext)); }
public static async Task <WebBoolResult> ProcessAsync(IOwinContext context, string identifier) { bool result = true; if (identifier == null) { Logger.Debug("StopStream: Identifier is null"); result = false; } if (!(await StreamControl.StopStreamingAsync(identifier))) { Logger.Debug("StopStream: Unknown identifier: {0}", identifier); result = false; } return(new WebBoolResult { Result = result }); }
public void UpdateAllStreamLocations() { // Get a list of all stream controls. We can't actually update the stream locations // within this foreach loop because the update may alter the child collection and // cause an exception to be thrown. List <StreamControl> streams = new List <StreamControl>(); foreach (UIElement uie in Children) { StreamControl sc = uie as StreamControl; if (null != sc) { streams.Add(sc); } } // Now we can actually do the updates foreach (StreamControl sc in streams) { sc.UpdateStreamLocation(); } }
public void UpdateLineToParent() { if (null == m_lineToParent || null == m_commentParent) { return; } Point location; StreamControl stream = m_commentParent as StreamControl; if (null == stream) { location = (m_commentParent as ProcessUnitControl).Location; } else { location = stream.StreamLineMidpoint; } m_lineToParent.X1 = Location.X; m_lineToParent.Y1 = Location.Y; m_lineToParent.X2 = location.X; m_lineToParent.Y2 = location.Y; }
public static async Task <WebStringResult> ProcessAsync(IOwinContext context, string identifier, string profileName, long startPosition, int audioId = -1, int subtitleId = -1) { if (identifier == null) { throw new BadRequestException("StartStreamWithStreamSelection: identifier is null"); } if (profileName == null) { throw new BadRequestException("StartStreamWithStreamSelection: profileName is null"); } EndPointProfile profile = null; List <EndPointProfile> namedProfiles = ProfileManager.Profiles.Where(x => x.Value.Name == profileName).Select(namedProfile => namedProfile.Value).ToList(); if (namedProfiles.Count > 0) { profile = namedProfiles[0]; } else if (ProfileManager.Profiles.ContainsKey(profileName)) { profile = ProfileManager.Profiles[profileName]; } if (profile == null) { throw new BadRequestException(string.Format("StartStreamWithStreamSelection: unknown profile: {0}", profileName)); } StreamItem streamItem = await StreamControl.GetStreamItemAsync(identifier); if (streamItem == null) { throw new BadRequestException(string.Format("StartStreamWithStreamSelection: unknown identifier: {0}", identifier)); } streamItem.Profile = profile; streamItem.StartPosition = startPosition; if (streamItem.RequestedMediaItem is LiveTvMediaItem) { streamItem.StartPosition = 0; } Guid?userId = ResourceAccessUtils.GetUser(context); streamItem.TranscoderObject = new ProfileMediaItem(identifier, profile, streamItem.IsLive); await streamItem.TranscoderObject.Initialize(userId, streamItem.RequestedMediaItem, audioId >= 0?audioId : (int?)null, subtitleId); if ((streamItem.TranscoderObject.TranscodingParameter is VideoTranscoding vt)) { vt.HlsBaseUrl = string.Format("RetrieveStream?identifier={0}&hls=", identifier); } await StreamControl.StartStreamingAsync(identifier, startPosition); string filePostFix = "&file=media.ts"; if (profile.MediaTranscoding != null && profile.MediaTranscoding.VideoTargets != null) { foreach (var target in profile.MediaTranscoding.VideoTargets) { if (target.Target.VideoContainerType == VideoContainer.Hls) { filePostFix = "&file=manifest.m3u8"; //Must be added for some clients to work (Android mostly) break; } } } string url = GetBaseStreamUrl.GetBaseStreamURL(context) + "/MPExtended/StreamingService/stream/RetrieveStream?identifier=" + identifier + filePostFix; return(new WebStringResult { Result = url }); }
public static async Task <bool> ProcessAsync(IOwinContext context, string identifier, string file, string hls) { Stream resourceStream = null; bool onlyHeaders = false; if (identifier == null) { throw new BadRequestException("RetrieveStream: Identifier is null"); } StreamItem streamItem = await StreamControl.GetStreamItemAsync(identifier); if (streamItem == null) { throw new BadRequestException("RetrieveStream: Identifier is not valid"); } if (!streamItem.IsActive) { Logger.Debug("RetrieveStream: Stream for {0} is no longer active", identifier); SetErrorStatus(context, "Stream is no longer active"); return(true); } long startPosition = streamItem.StartPosition; if (hls != null) { #region Handle segment/playlist request if (await SendSegmentAsync(hls, context, streamItem)) { return(true); } if (streamItem.ItemType != Common.WebMediaType.TV && streamItem.ItemType != Common.WebMediaType.Radio && MediaConverter.GetSegmentSequence(hls) >= 0) { long segmentRequest = MediaConverter.GetSegmentSequence(hls); if (await streamItem.RequestSegmentAsync(segmentRequest) == false) { Logger.Error("RetrieveStream: Request for segment file {0} canceled", hls); SetErrorStatus(context, "Request for segment file canceled"); return(true); } startPosition = segmentRequest * MediaConverter.HLSSegmentTimeInSeconds; } else { Logger.Error("RetrieveStream: Unable to find segment file {0}", hls); SetErrorStatus(context, "Unable to find segment file"); return(true); } #endregion } #region Init response // Grab the mimetype from the media item and set the Content Type header. if (streamItem.TranscoderObject.Mime == null) { throw new InternalServerException("RetrieveStream: Media item has bad mime type, re-import media item"); } context.Response.ContentType = streamItem.TranscoderObject.Mime; TransferMode mediaTransferMode = TransferMode.Interactive; if (streamItem.TranscoderObject.IsVideo || streamItem.TranscoderObject.IsAudio) { mediaTransferMode = TransferMode.Streaming; } StreamMode requestedStreamingMode = StreamMode.Normal; string byteRangesSpecifier = context.Request.Headers["Range"]; if (byteRangesSpecifier != null) { Logger.Debug("RetrieveStream: Requesting range {1} for mediaitem {0}", streamItem.RequestedMediaItem.MediaItemId, byteRangesSpecifier); requestedStreamingMode = StreamMode.ByteRange; } #endregion #region Process range request if (streamItem.StreamContext is TranscodeContext tc && (streamItem.TranscoderObject.IsTranscoding == false || (tc.Partial == false && tc.TargetFileSize > 0 && tc.TargetFileSize > streamItem.TranscoderObject.Metadata.Size))) { streamItem.TranscoderObject.Metadata.Size = tc.TargetFileSize; } IList <Range> ranges = null; Range timeRange = new Range(startPosition, 0); Range byteRange = null; if (requestedStreamingMode == StreamMode.ByteRange) { long lSize = GetStreamSize(streamItem.TranscoderObject); ranges = ParseByteRanges(byteRangesSpecifier, lSize); if (ranges == null || ranges.Count == 0) { //At least 1 range is needed context.Response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable; context.Response.ContentLength = 0; context.Response.ContentType = null; Logger.Debug("RetrieveStream: Sending headers: " + string.Join(";", context.Response.Headers.Select(x => x.Key + "=" + x.Value).ToArray())); return(true); } } if (streamItem.TranscoderObject.IsSegmented == false && streamItem.TranscoderObject.IsTranscoding == true && mediaTransferMode == TransferMode.Streaming) { if ((requestedStreamingMode == StreamMode.ByteRange) && (ranges == null || ranges.Count == 0)) { //At least 1 range is needed context.Response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable; context.Response.ContentLength = 0; context.Response.ContentType = null; Logger.Debug("RetrieveStream: Sending headers: " + string.Join(";", context.Response.Headers.Select(x => x.Key + "=" + x.Value).ToArray())); return(true); } } if (ranges != null && ranges.Count > 0) { //Use only last range if (requestedStreamingMode == StreamMode.ByteRange) { byteRange = ranges[ranges.Count - 1]; timeRange = ConvertToTimeRange(byteRange, streamItem.TranscoderObject); } } #endregion #region Handle ready file request // The initial request? if (resourceStream == null && streamItem.StreamContext != null && (streamItem.StartPosition == timeRange.From || file != null)) { resourceStream = streamItem.StreamContext.Stream; } if (resourceStream == null && streamItem.TranscoderObject.IsTranscoded == false) { var streamContext = await StreamControl.StartOriginalFileStreamingAsync(identifier); resourceStream = streamContext?.Stream; } #endregion #region Handle transcode bool partialResource = false; if (resourceStream == null) { Logger.Debug("RetrieveStream: Attempting to start streaming for mediaitem {0} in mode {1}", streamItem.RequestedMediaItem.MediaItemId, requestedStreamingMode.ToString()); var transcode = await StreamControl.StartStreamingAsync(identifier, timeRange.From); partialResource = transcode?.Partial ?? false; resourceStream = transcode?.Stream; //Send any HLS file originally requested if (hls != null && await SendSegmentAsync(hls, context, streamItem)) { return(true); } } if (!streamItem.TranscoderObject.IsStreamable) { Logger.Debug("RetrieveStream: Live transcoding of mediaitem {0} is not possible because of media container", streamItem.RequestedMediaItem.MediaItemId); } #endregion #region Finish and send response // HTTP/1.1 RFC2616 section 14.25 'If-Modified-Since' if (!string.IsNullOrEmpty(context.Request.Headers["If-Modified-Since"])) { DateTime lastRequest = DateTime.Parse(context.Request.Headers["If-Modified-Since"]); if (lastRequest.CompareTo(streamItem.TranscoderObject.LastUpdated) <= 0) { context.Response.StatusCode = (int)HttpStatusCode.NotModified; } } // HTTP/1.1 RFC2616 section 14.29 'Last-Modified' context.Response.Headers["Last-Modified"] = streamItem.TranscoderObject.LastUpdated.ToUniversalTime().ToString("r"); if (resourceStream == null) { context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.ReasonPhrase = "No resource stream found"; context.Response.ContentLength = 0; context.Response.ContentType = null; return(true); } using (await streamItem.RequestBusyLockAsync(SendDataCancellation.Token)) { // TODO: fix method onlyHeaders = context.Request.Method == "HEAD" || context.Response.StatusCode == (int)HttpStatusCode.NotModified; if (requestedStreamingMode == StreamMode.ByteRange) { if (ranges != null && ranges.Count > 0) { // We only support last range await SendByteRangeAsync(context, resourceStream, streamItem.TranscoderObject, streamItem.Profile, ranges[ranges.Count - 1], onlyHeaders, partialResource, mediaTransferMode); return(true); } } Logger.Debug("RetrieveStream: Sending file header only: {0}", onlyHeaders.ToString()); await SendWholeFileAsync(context, resourceStream, streamItem.TranscoderObject, streamItem.Profile, onlyHeaders, partialResource, mediaTransferMode); } #endregion return(true); }
public static async Task <WebBoolResult> ProcessAsync(IOwinContext context, string itemId, string clientDescription, string identifier, WebMediaType type, int?idleTimeout) { if (itemId == null) { throw new BadRequestException("InitStream: itemId is null"); } if (clientDescription == null) { throw new BadRequestException("InitStream: clientDescription is null"); } if (identifier == null) { throw new BadRequestException("InitStream: identifier is null"); } StreamItem streamItem = new StreamItem { ItemType = type, ClientDescription = clientDescription, IdleTimeout = idleTimeout ?? -1, ClientIp = context.Request.RemoteIpAddress }; MediaItem mediaItem = new LiveTvMediaItem(Guid.Empty); if (streamItem.ItemType == WebMediaType.TV || streamItem.ItemType == WebMediaType.Radio) { int channelIdInt; if (!int.TryParse(itemId, out channelIdInt)) { throw new BadRequestException(string.Format("InitStream: Couldn't convert channelId to int: {0}", itemId)); } streamItem.Title = "Live TV"; if (streamItem.ItemType == WebMediaType.Radio) { streamItem.Title = "Live Radio"; } streamItem.LiveChannelId = channelIdInt; var info = await MediaAnalyzer.ParseChannelStreamAsync(channelIdInt, (LiveTvMediaItem)mediaItem); if (info == null) { throw new BadRequestException(string.Format("InitStream: Couldn't parse channel stream: {0}", channelIdInt)); } } else { Guid itemGuid; if (!Guid.TryParse(itemId, out itemGuid)) { throw new BadRequestException(string.Format("InitStream: Couldn't parse itemId: {0}", itemId)); } ISet <Guid> necessaryMIATypes = new HashSet <Guid>(); necessaryMIATypes.Add(MediaAspect.ASPECT_ID); necessaryMIATypes.Add(ProviderResourceAspect.ASPECT_ID); ISet <Guid> optionalMIATypes = new HashSet <Guid>(); optionalMIATypes.Add(VideoAspect.ASPECT_ID); optionalMIATypes.Add(AudioAspect.ASPECT_ID); optionalMIATypes.Add(ImageAspect.ASPECT_ID); mediaItem = MediaLibraryAccess.GetMediaItemById(context, itemGuid, necessaryMIATypes, optionalMIATypes); if (mediaItem == null) { throw new NotFoundException(string.Format("InitStream: Couldn't init stream! No MediaItem found with id: {0}", itemId)); } streamItem.Title = mediaItem[MediaAspect.Metadata.AspectId][0].GetAttributeValue <string>(MediaAspect.ATTR_TITLE); } streamItem.RequestedMediaItem = mediaItem; // Add the stream to the stream controller bool result = await StreamControl.AddStreamItemAsync(identifier, streamItem); return(new WebBoolResult { Result = result }); }
public void Shutdown() { SaveSettings(); BaseSendData.SendDataCancellation.Cancel(); StreamControl.StopStreamCleanupTask(true); }