Example #1
0
        protected static Range ConvertToTimeRange(Range byteRange, ProfileMediaItem item)
        {
            if (byteRange.Length <= 0.0)
            {
                return(new Range(0, Convert.ToInt64(item.Metadata.Duration)));
            }

            double startSeconds = 0;
            double endSeconds   = 0;

            if (item.IsTranscoding == true)
            {
                long   length = GetStreamSize(item);
                double factor = Convert.ToDouble(item.Metadata.Duration) / Convert.ToDouble(length);
                startSeconds = Convert.ToDouble(byteRange.From) * factor;
                endSeconds   = Convert.ToDouble(byteRange.To) * factor;
            }
            else
            {
                double bitrate = 0;
                if (item.IsSegmented == false)
                {
                    bitrate = Convert.ToDouble(item.Metadata.Bitrate) * 1024; //Bitrate in bits/s
                }
                if (bitrate > 0)
                {
                    startSeconds = Convert.ToDouble(byteRange.From) / (bitrate / 8.0);
                    endSeconds   = Convert.ToDouble(byteRange.To) / (bitrate / 8.0);
                }
            }
            return(new Range(Convert.ToInt64(startSeconds), Convert.ToInt64(endSeconds)));
        }
Example #2
0
        protected static Range ConvertToByteRange(Range timeRange, ProfileMediaItem item)
        {
            if (timeRange.Length <= 0.0)
            {
                return(new Range(0, item.Metadata.Size ?? 0));
            }
            long startByte = 0;
            long endByte   = 0;

            if (item.IsTranscoding == true)
            {
                long   length = GetStreamSize(item);
                double factor = Convert.ToDouble(length) / Convert.ToDouble(item.Metadata.Duration);
                startByte = Convert.ToInt64(Convert.ToDouble(timeRange.From) * factor);
                endByte   = Convert.ToInt64(Convert.ToDouble(timeRange.To) * factor);
            }
            else
            {
                double bitrate = 0;
                if (item.IsSegmented == false)
                {
                    bitrate = Convert.ToDouble(item.Metadata.Bitrate) * 1024; //Bitrate in bits/s
                }
                startByte = Convert.ToInt64((bitrate * timeRange.From) / 8.0);
                endByte   = Convert.ToInt64((bitrate * timeRange.To) / 8.0);
            }
            return(new Range(startByte, endByte));
        }
Example #3
0
        protected static Range ConvertToFileRange(Range requestedByteRange, ProfileMediaItem item, long length)
        {
            long toRange   = requestedByteRange.To;
            long fromRange = requestedByteRange.From;

            if (toRange <= 0 || toRange > length)
            {
                toRange = length;
            }
            if (item.IsSegmented == false && item.IsTranscoding == true)
            {
                if (item.Metadata.Size > 0 && (toRange > item.Metadata.Size || fromRange > item.Metadata.Size))
                {
                    fromRange = Convert.ToInt64((Convert.ToDouble(fromRange) / Convert.ToDouble(length)) * Convert.ToDouble(item.Metadata.Size));
                    toRange   = Convert.ToInt64((Convert.ToDouble(toRange) / Convert.ToDouble(length)) * Convert.ToDouble(item.Metadata.Size));
                }
            }
            return(new Range(fromRange, toRange));
        }
Example #4
0
        internal static long GetStreamSize(ProfileMediaItem item)
        {
            long length = item?.Metadata?.Size ?? 0;

            if (item.IsTranscoding == true || item.IsLive == true || length <= 0)
            //if (length <= 0)
            {
                if (item.IsAudio)
                {
                    return(TRANSCODED_AUDIO_STREAM_MAX);
                }
                else if (item.IsImage)
                {
                    return(TRANSCODED_IMAGE_STREAM_MAX);
                }
                else if (item.IsVideo)
                {
                    return(TRANSCODED_VIDEO_STREAM_MAX);
                }
                return(TRANSCODED_VIDEO_STREAM_MAX);
            }
            return(length);
        }
Example #5
0
        protected static async Task SendByteRangeAsync(IOwinContext context, Stream resourceStream, ProfileMediaItem item, EndPointProfile profile, Range range, bool onlyHeaders, bool partialResource, TransferMode mediaTransferMode)
        {
            if (range.From > 0 && range.From == range.To)
            {
                context.Response.StatusCode    = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
                context.Response.ContentLength = 0;
                context.Response.ContentType   = null;
                return;
            }
            long length = range.Length;

            if (item.IsSegmented == false && item.IsTranscoding == true)
            {
                length = GetStreamSize(item);
            }
            else
            {
                length = resourceStream.Length;
            }
            Range fileRange = ConvertToFileRange(range, item, length);

            if (fileRange.From < 0 || length <= fileRange.From)
            {
                context.Response.StatusCode    = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
                context.Response.ContentLength = 0;
                context.Response.ContentType   = null;
                return;
            }
            if (partialResource == false && await WaitForMinimumFileSizeAsync(resourceStream, fileRange.From) == false)
            {
                context.Response.StatusCode    = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
                context.Response.ContentLength = 0;
                context.Response.ContentType   = null;
                return;
            }
            if (range.From > length || range.To > length)
            {
                range = fileRange;
            }

            context.Response.StatusCode = (int)HttpStatusCode.PartialContent;

            if (item.IsLive || range.Length == 0 ||
                (mediaTransferMode == TransferMode.Streaming && context.Request.Protocol == "HTTP/1.1" && profile.Settings.Communication.AllowChunckedTransfer))
            {
                context.Response.Headers["Content-Range"] = $"bytes {range.From}-";
                context.Response.ContentLength            = null;
            }
            else if (length <= 0)
            {
                context.Response.Headers["Content-Range"] = $"bytes {range.From}-{range.To - 1}";
                context.Response.ContentLength            = range.Length;
            }
            else
            {
                context.Response.Headers["Content-Range"] = $"bytes {range.From}-{range.To - 1}/{length}";
                context.Response.ContentLength            = range.Length;
            }
            if (item.IsLive == false)
            {
                context.Response.Headers["X-Content-Duration"] = Convert.ToDouble(item.Metadata.Duration).ToString("0.00", CultureInfo.InvariantCulture);
                context.Response.Headers["Content-Duration"]   = Convert.ToDouble(item.Metadata.Duration).ToString("0.00", CultureInfo.InvariantCulture);
            }

            await SendAsync(context, resourceStream, item, profile, onlyHeaders, partialResource, fileRange);
        }
Example #6
0
        protected static async Task SendAsync(IOwinContext context, Stream resourceStream, ProfileMediaItem item, EndPointProfile profile, bool onlyHeaders, bool partialResource, Range byteRange)
        {
            if (onlyHeaders)
            {
                return;
            }

            bool clientDisconnected = false;
            Guid streamID           = item.StartStreaming();

            if (streamID == Guid.Empty)
            {
                Logger.Error("BaseSendData: Unable to start stream");
                return;
            }
            try
            {
                if (context.Response.ContentLength == 0)
                {
                    //Not allowed to have a content length of zero
                    context.Response.ContentLength = null;
                }
                Logger.Debug("BaseSendData: Sending chunked: {0}", context.Response.ContentLength == null);
                string clientID   = context.Request.RemoteIpAddress;
                int    bufferSize = profile.Settings.Communication.DefaultBufferSize;
                if (bufferSize <= 0)
                {
                    bufferSize = 1500;
                }
                byte[] buffer = new byte[bufferSize];
                int    bytesRead;
                long   count       = 0;
                bool   isStream    = false;
                long   waitForSize = 0;
                if (byteRange.Length == 0 || (byteRange.Length > 0 && byteRange.Length >= profile.Settings.Communication.InitialBufferSize))
                {
                    waitForSize = profile.Settings.Communication.InitialBufferSize;
                }
                if (partialResource == false)
                {
                    if (waitForSize < byteRange.From)
                    {
                        waitForSize = byteRange.From;
                    }
                }
                if (await WaitForMinimumFileSizeAsync(resourceStream, waitForSize) == false)
                {
                    Logger.Error("BaseSendData: Unable to send stream because of invalid length: {0} ({1} required)", resourceStream.Length, waitForSize);
                    return;
                }

                long start = 0;
                if (partialResource == false)
                {
                    start = byteRange.From;
                }
                if (resourceStream.CanSeek)
                {
                    resourceStream.Seek(start, SeekOrigin.Begin);
                }
                long length = byteRange.Length;
                if (length <= 0 || item.IsLive || (item.IsSegmented == false && item.IsTranscoding == true))
                {
                    isStream = true;
                }
                int emptyCount = 0;
                while (item.IsStreamActive(streamID))
                {
                    if (isStream)
                    {
                        if (resourceStream.CanSeek)
                        {
                            length = resourceStream.Length - count;
                        }
                        else
                        {
                            length = bufferSize; //Keep stream alive
                        }
                    }
                    bytesRead = await resourceStream.ReadAsync(buffer, 0, length > bufferSize?bufferSize : (int)length);

                    count += bytesRead;

                    if (bytesRead > 0)
                    {
                        emptyCount = 0;
                        try
                        {
                            //Send fetched bytes
                            await context.Response.WriteAsync(buffer, 0, bytesRead, SendDataCancellation.Token);
                        }
                        catch (Exception)
                        {
                            // Client disconnected
                            Logger.Debug("BaseSendData: Connection lost after {0} bytes", count);
                            clientDisconnected = true;
                            break;
                        }
                        length -= bytesRead;

                        if (isStream == false && length <= 0)
                        {
                            //All bytes in the requested range sent
                            break;
                        }
                    }
                    else
                    {
                        emptyCount++;
                        if (emptyCount > 2)
                        {
                            Logger.Debug("BaseSendData: Buffer underrun delay");
                            await Task.Delay(100);
                        }
                        if (emptyCount > 10)
                        {
                            //Stream is not getting any bigger
                            break;
                        }
                    }

                    if (resourceStream.CanSeek)
                    {
                        if (item.IsTranscoding == false && resourceStream.Position == resourceStream.Length)
                        {
                            //No more data will be available
                            break;
                        }
                    }
                }
            }
            finally
            {
                item.StopStreaming(streamID);

                if (clientDisconnected || item.IsSegmented == false)
                {
                    if (clientDisconnected == false)
                    {
                        //Everything sent to client so presume watched
                        if (item.IsLive == false)
                        {
                            Guid?         userId  = ResourceAccessUtils.GetUser(context);
                            IMediaLibrary library = ServiceRegistration.Get <IMediaLibrary>();
                            if (library != null && userId.HasValue)
                            {
                                library.NotifyUserPlayback(userId.Value, item.MediaItemId, 100, true);
                            }
                        }
                    }
                }
                Logger.Debug("BaseSendData: Sending complete");
            }
        }
Example #7
0
        protected static async Task SendWholeFileAsync(IOwinContext context, Stream resourceStream, ProfileMediaItem item, EndPointProfile profile, bool onlyHeaders, bool partialResource, TransferMode mediaTransferMode)
        {
            if (await WaitForMinimumFileSizeAsync(resourceStream, 1) == false)
            {
                context.Response.StatusCode    = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
                context.Response.ContentLength = 0;
                context.Response.ContentType   = null;
                Logger.Debug("BaseSendData: Sending headers: " + string.Join(";", context.Response.Headers.Select(x => x.Key + "=" + x.Value).ToArray()));
                return;
            }

            long length = GetStreamSize(item);

            if (resourceStream.CanSeek == true && (item.IsTranscoding == false || item.IsSegmented == true))
            {
                length = resourceStream.Length;
            }

            if (resourceStream.CanSeek == false && context.Request.Protocol == "HTTP/1.1" && profile.Settings.Communication.AllowChunckedTransfer)
            {
                context.Response.StatusCode    = (int)HttpStatusCode.PartialContent;
                context.Response.ContentLength = null;
            }
            else
            {
                context.Response.StatusCode    = (int)HttpStatusCode.OK;
                context.Response.ContentLength = length;
            }

            Range byteRange = new Range(0, context.Response.ContentLength ?? 0);

            await SendAsync(context, resourceStream, item, profile, onlyHeaders, partialResource, byteRange);
        }
Example #8
0
        public static Task <WebResolution> ProcessAsync(IOwinContext context, WebMediaType type, int?provider, string itemId, int?offset, string profileName)
        {
            if (itemId == null)
            {
                throw new BadRequestException("GetStreamSize: itemId is null");
            }
            if (profileName == null)
            {
                throw new BadRequestException("GetStreamSize: profileName is null");
            }

            ISet <Guid> necessaryMIATypes = new HashSet <Guid>();

            necessaryMIATypes.Add(MediaAspect.ASPECT_ID);
            necessaryMIATypes.Add(ProviderResourceAspect.ASPECT_ID);
            necessaryMIATypes.Add(ImporterAspect.ASPECT_ID);

            ISet <Guid> optionalMIATypes = new HashSet <Guid>();

            optionalMIATypes.Add(VideoAspect.ASPECT_ID);
            optionalMIATypes.Add(VideoStreamAspect.ASPECT_ID);
            optionalMIATypes.Add(VideoAudioStreamAspect.ASPECT_ID);
            optionalMIATypes.Add(ImageAspect.ASPECT_ID);

            var item = MediaLibraryAccess.GetMediaItemById(context, itemId, necessaryMIATypes, optionalMIATypes);

            if (item == null)
            {
                throw new NotFoundException(String.Format("GetStreamSize: No MediaItem found with id: {0}", itemId));
            }

            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("GetStreamSize: unknown profile: {0}", profileName));
            }

            var target = new ProfileMediaItem(Guid.NewGuid().ToString(), profile, false);

            var output = new WebResolution();

            if (target.IsImage)
            {
                output.Height = target.Image.Height ?? 0;
                output.Width  = target.Image.Width ?? 0;
            }
            else if (target.IsVideo)
            {
                output.Height = target.Image.Height ?? 0;
                output.Width  = target.Image.Width ?? 0;
            }

            return(Task.FromResult(output));
        }