Example #1
0
        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
            });
        }
Example #2
0
        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
            });
        }
Example #3
0
        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);
        }
Example #6
0
        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));
        }
Example #7
0
        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
            });
        }
Example #8
0
        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();
            }
        }
Example #9
0
        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;
        }
Example #10
0
        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
            });
        }
Example #11
0
        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);
        }
Example #12
0
        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
            });
        }
Example #13
0
 public void Shutdown()
 {
     SaveSettings();
     BaseSendData.SendDataCancellation.Cancel();
     StreamControl.StopStreamCleanupTask(true);
 }