private async Task ReadBodyAsync(HttpRequest request, MediaSegment mediaSegment, bool expire)
        {
            int count = 0;

            while (true)
            {
                var(buffer, endOfStream) = await ReadBufferAsync(request);

                var cacheKey = mediaSegment.GetChunkKey(count++);
                var options  = new MemoryCacheEntryOptions
                {
                    SlidingExpiration = expire ? TimeSpan.FromSeconds(60) : (TimeSpan?)null,
                    Priority          = expire ? CacheItemPriority.Normal : CacheItemPriority.NeverRemove,
                    Size = BUFFER_SIZE
                };
                options.RegisterPostEvictionCallback(RemoveCacheEntry);
                _cache.Set <MediaBuffer>(cacheKey, buffer, options);
                _logger.LogDebug($"Got buffer of size: {buffer.Length} for {cacheKey} ");
                mediaSegment.AddBuffer(buffer.Length, endOfStream);
                if (endOfStream)
                {
                    break;
                }
            }
        }
        private async Task SendResponse(HttpResponse response, MediaSegment mediaSegment)
        {
            response.StatusCode = 200;
            var length = mediaSegment.Length;

            if (mediaSegment.Complete)
            {
                response.ContentLength = mediaSegment.Length;
                _logger.LogWarning($"Sending full response for {mediaSegment.Path} length:{mediaSegment.Length} ");
            }
            else
            {
                _logger.LogWarning($"Chunked transfer encoding for {mediaSegment.Path}");
            }
            response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
            {
                Public  = true,
                NoCache = true,
                NoStore = true
            };

            foreach (var index in mediaSegment.GetBufferIndex())
            {
                var cacheKey = mediaSegment.GetChunkKey(index);
                var buffer   = _cache.Get <MediaBuffer>(cacheKey);
                if (buffer == null)
                {
                    throw new InvalidOperationException($"Missing cache entry for {cacheKey}");
                }
                await response.Body.WriteAsync(buffer.Memory);

                await response.Body.FlushAsync();
            }
        }
 public MediaSearchEventArgs(Media media, MediaSegment segment, string mediaName, TimeSpan tCIn, TimeSpan duration)
 {
     Media = media;
     MediaSegment = segment;
     MediaName = mediaName;
     TCIn = tCIn;
     Duration = duration;
 }
Esempio n. 4
0
        private void MakeTimelineInfo(IXmlNode segmentTimeline, uint bitrate,
                                      ulong timescale, string repID,
                                      uint startNumber, ulong presentationTimeOffset,
                                      string mediaFormat, ref MediaStream stream)
        {
            var timeline = segmentTimeline.ChildNodes.Where(x => x.NodeName == "S");

            uint  segmentNumber = startNumber;
            ulong time          = 0;
            //Set time equal to the @t attribute of first element, otherwise it is 0.
            //This is to replace the value of $Time$ in the media segment url
            var tAttribute = timeline.First().Attributes.GetNamedItem("t");

            if (tAttribute != null)
            {
                time = ulong.Parse(tAttribute.InnerText);
            }
            foreach (var t in timeline)
            {
                ulong duration        = ulong.Parse(t.Attributes.GetNamedItem("d").InnerText);
                var   repeatAttribute = t.Attributes.GetNamedItem("r");
                long  repeats         = 0;
                if (repeatAttribute != null)
                {
                    repeats = long.Parse(repeatAttribute.InnerText);
                }

                for (int i = 0; i < repeats + 1; i++)
                {
                    MediaSegment segment = new MediaSegment();
                    segment.Duration  = duration;
                    segment.Timestamp = time - presentationTimeOffset;
                    segment.Number    = segmentNumber;

                    //Construct the mediaURL
                    string mediaSegmentUrl;
                    ExpandDASHUrlSegmentTemplate(mediaFormat, manifest.BaseUrl, bitrate,
                                                 repID, segmentNumber, time, out mediaSegmentUrl);
                    segment.SegmentUrl = mediaSegmentUrl;

                    stream.PushBackSegment(segment);
                    time += duration;
                    segmentNumber++;
                }
            }

            double msStreamDuration = (double)time * (1000) / timescale;

#if DEBUG
            Logger.Log("Found " + stream.Segments.Count() + " segments - Duration: " + msStreamDuration.ToString() + " miliseconds");
#endif

            if (msStreamDuration > manifest.MediaPresentationDuration.TotalMilliseconds)
            {
                manifest.MediaPresentationDuration = TimeSpan.FromMilliseconds(msStreamDuration);
            }
        }
        private MediaBuffer GetMediaBuffer(MediaSegment mediaSegment, int index)
        {
            var cacheKey = mediaSegment.GetChunkKey(index);
            var buffer   = _cache.Get <MediaBuffer>(cacheKey);

            if (buffer == null)
            {
                throw new InvalidOperationException($"Missing cache entry for {cacheKey}");
            }
            return(buffer);
        }
        private async void SearchSegmentIfOutsideBuffer()
        {
            MediaSegment        segment  = ((LiveSourceBufferManager)GetVideoBuffer()).GetNextSegment();
            HttpResponseMessage response = await Downloader.SendHeadRequestAsync(new Uri(segment.SegmentUrl));

            if (response.StatusCode == HttpStatusCode.NotFound)
            {
#if DEBUG
                Logger.Log("Trying to go to Live Edge");
#endif
                GoToLiveEdge();
            }
        }
        private async Task SendResponse(HttpRequest request, HttpResponse response, MediaSegment mediaSegment)
        {
            response.Headers.Add("Accept-Ranges", "bytes");
            if (mediaSegment.Complete)
            {
                var length = mediaSegment.Length;
                response.ContentLength = mediaSegment.Length;
                _logger.LogInformation($"Sending complete segment for {mediaSegment.Path} length:{mediaSegment.Length} ");
            }
            else
            {
                _logger.LogWarning($"Chunked transfer encoding for {mediaSegment.Path}");
            }

            response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
            {
                Public  = true,
                NoCache = true,
                NoStore = true
            };

            // check for byte range.
            var values = request.Headers["Range"];

            if (values.Count > 0)
            {
                var rangeHeader = RangeHeaderValue.Parse(values[0]);
                response.StatusCode = 206;
                var range  = rangeHeader.Ranges.Single(); // only one range is supported.
                var offset = range.From ?? 0;
                var length = range.To.HasValue ? range.To.Value - offset : long.MaxValue;
                length = mediaSegment.Complete ? Math.Min(length, mediaSegment.Length) : length;
                response.Headers.ContentLength = length;
                var lengthString = mediaSegment.Complete ? mediaSegment.Length.ToString() : "*";
                response.Headers.Add("Content-Range", $"bytes {range.From}-{range.To}/{lengthString}");
                foreach (var buffer in GetRangeBuffers(mediaSegment, (int)offset, (int)length))
                {
                    await SendBufferAsync(response, buffer, !mediaSegment.Complete);
                }
            }
            else
            {
                response.StatusCode = 200;

                foreach (var index in mediaSegment.GetBufferIndex())
                {
                    var buffer = GetMediaBuffer(mediaSegment, index);
                    await SendBufferAsync(response, buffer.Memory, flush : !mediaSegment.Complete);
                }
            }
        }
        public async Task InvokeAsync(HttpContext context)
        {
            _logger.LogDebug(
                $"{context.Request.Method} {context.Request.Path} type: {context.Request.ContentType}  size: {context.Request.Headers.ContentLength}  Encoding:{context.Request.Headers["Transfer-Encoding"]}");

            var    options     = new MemoryCacheEntryOptions();
            var    path        = context.Request.Path.Value;
            string contentType = context.Request.ContentType;
            var    expire      = false;

            if (path.Contains("/chunk"))
            {
                contentType = CmafSegmentContentType;
                options.SetSlidingExpiration(TimeSpan.FromSeconds(60));
                expire = true;
            }
            else if (path.Contains("/init"))
            {
                contentType      = CmafSegmentContentType;
                options.Priority = CacheItemPriority.NeverRemove;
            }
            else if (path.EndsWith(".mpd"))
            {
                contentType      = ManifestContentType;
                options.Priority = CacheItemPriority.NeverRemove;
            }
            else if (path.EndsWith(".m3u8"))
            {
                contentType      = HlsContentType;
                options.Priority = CacheItemPriority.NeverRemove;
            }

            var mediaBuffer = new MediaSegment(context.Request.Path.Value, contentType, _logger);

            _cache.Set <MediaSegment>(context.Request.Path, mediaBuffer, options);

            try
            {
                await ReadBodyAsync(context.Request, mediaBuffer, expire);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to read body for {context.Request.Path}");
                _cache.Remove(context.Request.Path);
            }

            _logger.LogInformation($"completed {context.Request.Path} size:{mediaBuffer.Length} type:{contentType} ");
            context.Response.StatusCode = 201;
        }
Esempio n. 9
0
        internal MediaSegment GetNextSegment()
        {
            DetermineStartingSegmentIndex();
            bool isEndOfStream = false;

            if (manifest.HasPresentationDuration)
            {
                if (isFirstSegment)
                {
                    var segmentInfo          = stream.SegmentInformation;
                    var presentationDuration = (ulong)manifest.MediaPresentationDuration.TotalMilliseconds * segmentInfo.Timescale / 1000;

                    var segmentDuration = segmentInfo.Duration;

                    totalSegmentCount = (int)presentationDuration / (int)segmentDuration;
                }
            }

            MediaSegment segment;

            if (stream.CanGenerateSegmentsDynamically && !isEndOfStream)
            {
                //create new segment based on media segment info given
                segment = new MediaSegment();
                var segmentInfo = stream.SegmentInformation;

                var format  = segmentInfo.UrlTemplate;
                var bitrate = segmentInfo.Bitrate;
                var repID   = segmentInfo.RepresentationID;
                var number  = segmentInfo.StartNumber + (ulong)nextSegmentIndex;
                var time    = segmentInfo.StartTimestamp + (ulong)nextSegmentIndex;

                string mediaUrl;
                DashManifestParser.ExpandDASHUrlSegmentTemplate(format, manifest.BaseUrl, bitrate, repID, number, time, out mediaUrl);

                segment.Duration   = segmentInfo.Duration;
                segment.Number     = number;
                segment.Timestamp  = time;
                segment.SegmentUrl = mediaUrl;
            }
            else //populated by a list of segments
            {
                segment = stream.Segments.ElementAt(nextSegmentIndex);
            }
            nextSegmentNumber = segment.Number + 1;

            return(segment);
        }
Esempio n. 10
0
        private async Task DownloadAsync(MediaSegment segment, Stream outStream, int totalSegmentCount,
                                         Reference <long> overallDownloaded, Reference <int> downloadedSegmentCount)
        {
            using var client   = new HttpClient();
            using var response = await client.GetAsync(segment.Uri, HttpCompletionOption.ResponseHeadersRead);

            using var originalStream = await response.Content.ReadAsStreamAsync();

            using var stream = await _grabResult.WrapStreamAsync(originalStream);

            var contentLength = response.Content.Headers.ContentLength;

            if (contentLength == null)
            {
                await stream.CopyToAsync(outStream);

                return;
            }

            var avgSegmentSize = downloadedSegmentCount > 0 ? overallDownloaded / (double)downloadedSegmentCount : contentLength.Value;
            var downloaded     = 0L;
            var totalBytes     = avgSegmentSize * totalSegmentCount;

            const int BUFFER_LENGTH = 4096;
            var       buffer        = new byte[BUFFER_LENGTH];

            while (true)
            {
                var read = await stream.ReadAsync(buffer, 0, buffer.Length);

                if (read == 0)
                {
                    break;
                }
                await outStream.WriteAsync(buffer, 0, read);

                downloaded += read;
                _viewModel.DownloadProgress = (avgSegmentSize * downloadedSegmentCount + downloaded) / totalBytes;
            }

            overallDownloaded.Value += contentLength.Value;
            downloadedSegmentCount.Value++;
        }
        private IEnumerable <ReadOnlyMemory <byte> > GetRangeBuffers(MediaSegment segment, int offset, int length)
        {
            var curOffset = 0;

            foreach (var index in segment.GetBufferIndex())
            {
                var buffer = GetMediaBuffer(segment, index);
                if (offset > curOffset + buffer.Length)
                {
                    curOffset += buffer.Length;
                    continue;
                }

                var curLength = Math.Min(length, buffer.Length);
                yield return(buffer.Memory.Slice(offset - curOffset, curLength));

                length -= curLength;
                if (length == 0)
                {
                    break;
                }
            }
        }
Esempio n. 12
0
        private void MakeNumberBaseSegmentInfo(uint bitrate, UInt64 timescale,
                                               UInt64 segmentDuration, string repID,
                                               uint startNumber, string mediaFormat, ref MediaStream stream)
        {
            var    hnsPresentationDuration = (Convert.ToUInt64(manifest.MediaPresentationDuration.TotalMilliseconds) * Convert.ToUInt64(10000));
            var    hnsFragmentDuration     = ((segmentDuration * 10000000) / timescale);
            UInt64 count  = (uint)(hnsPresentationDuration / hnsFragmentDuration);
            UInt64 remain = (uint)(hnsPresentationDuration - (hnsFragmentDuration * count));

            if (manifest.IsLive && count == 0)
            {
                if (manifest.TimeShiftBufferDepth.TotalSeconds > 0 && hnsFragmentDuration > 0)
                {
                    var hnsTimeShiftBufferDepth = (ulong)(manifest.TimeShiftBufferDepth.TotalSeconds * 10000000);
                    count = hnsTimeShiftBufferDepth / hnsFragmentDuration;
                }
                else
                {
                    count = LiveSegmentCount;
                }
            }

            UInt64 time = 0;

            UInt64 segmentNumber = startNumber;

            if (manifest.IsLive)
            {
                CalculateNumberIdentifierForLive(hnsFragmentDuration, ref segmentNumber);
            }
            //Since we calculated the latest segment number, we will add to the front of the list
            for (UInt64 i = 0; i < count; i++)
            {
                MediaSegment segment = new MediaSegment();
                segment.Timestamp = time;
                segment.Duration  = segmentDuration;
                segment.Number    = segmentNumber;

                //Construct the mediaURL
                string mediaSegmentUrl;
                ExpandDASHUrlSegmentTemplate(mediaFormat, manifest.BaseUrl, bitrate,
                                             repID, segmentNumber, time, out mediaSegmentUrl);
                segment.SegmentUrl = mediaSegmentUrl;

                stream.PushBackSegment(segment);
                segmentNumber++;
                time += segmentDuration;
            }

            if (remain > 0)
            {
                MediaSegment segment = new MediaSegment();
                segment.Timestamp = time;
                segment.Duration  = remain / timescale;
                segment.Number    = segmentNumber;

                string mediaSegmentUrl;
                ExpandDASHUrlSegmentTemplate(mediaFormat, manifest.BaseUrl, bitrate,
                                             repID, segmentNumber, time, out mediaSegmentUrl);
                segment.SegmentUrl = mediaSegmentUrl;

                stream.PushBackSegment(segment);

#if DEBUG
                Logger.Log("Adding final segment to stream with MimeType:" + stream.MimeType + ". Segment{ Timestamp: " + time + "Duration: "
                           + segmentDuration + "Number: " + startNumber + "}"
                           + "\nURL: " + segment.SegmentUrl);
#endif
                time += segmentDuration;
            }
        }
 internal static ObservableSynchronizedCollection<MediaSegment> DbMediaSegmentsRead(this PersistentMedia media)
 {
     if (Connect())
     {
         Guid mediaGuid = media.MediaGuid;
         ObservableSynchronizedCollection<MediaSegment> segments = null;
         MediaSegment newMediaSegment;
         MySqlCommand cmd = new MySqlCommand("SELECT * FROM tas.MediaSegments where MediaGuid = @MediaGuid;", connection);
         cmd.Parameters.Add("@MediaGuid", MySqlDbType.Binary).Value = mediaGuid.ToByteArray();
         lock (connection)
         {
             if (_mediaSegments == null)
                 _mediaSegments = new Hashtable();
             segments = (ObservableSynchronizedCollection<MediaSegment>)_mediaSegments[mediaGuid];
             if (segments == null)
             {
                 segments = new ObservableSynchronizedCollection<MediaSegment>();
                 using (MySqlDataReader dataReader = cmd.ExecuteReader())
                 {
                     while (dataReader.Read())
                     {
                         newMediaSegment = new MediaSegment(mediaGuid)
                         {
                             idMediaSegment = dataReader.GetUInt64("idMediaSegment"),
                             SegmentName = (dataReader.IsDBNull(dataReader.GetOrdinal("SegmentName")) ? string.Empty : dataReader.GetString("SegmentName")),
                             TCIn = dataReader.IsDBNull(dataReader.GetOrdinal("TCIn")) ? default(TimeSpan) : dataReader.GetTimeSpan("TCIn"),
                             TCOut = dataReader.IsDBNull(dataReader.GetOrdinal("TCOut")) ? default(TimeSpan) : dataReader.GetTimeSpan("TCOut"),
                         };
                         segments.Add(newMediaSegment);
                     }
                     dataReader.Close();
                 }
                 _mediaSegments.Add(mediaGuid, segments);
             }
         }
         return segments;
     }
     else
         return null;
 }
Esempio n. 14
0
        protected override async void SendMediaSegment(TimeSpan now)
        {
            try
            {
                var end = now;

                if (!isInitialized || Appending || isEndOfStream)
                {
                    return;
                }

                //if the next segment is out of range from the manifest, then
                //try to redownload the manifest and pick up where we left off.
                //this is the case where a manifest reload didn't come in time,
                //we don't have a mediaPresentationDuration present, and we cannot generate
                //segments dynamically.the new manifest does not contain the segment,
                //then it is the end of stream.
                if (manifest.HasPresentationDuration && nextSegmentIndex > totalSegmentCount)
                {
                    //nextSegmentIndex = await GetNewSegmentIndexFromNewStream();

                    //if (nextSegmentIndex == -1)
                    //{
                    isEndOfStream = true;
                    return;
                    //}
                }

                foreach (var range in sb.Buffered)
                {
                    if (range.Start <= end + MaxGap && range.End > end)
                    {
                        end = range.End;
                    }
                    else if (range.Start > end)
                    {
                        break;
                    }
                }

                var stream = base.stream;

                MediaSegment segment = GetNextSegment();

                if (now + MaxBuffering <= end)
                {
                    return;
                }

                string url = segment.SegmentUrl;
                internalAppending = true;
#if DEBUG
                Logger.Log("Loading segment url " + url);
#endif
                Uri mediaSegmentUrl = null;
                try
                {
                    mediaSegmentUrl = new Uri(url);
                }
                catch (Exception e)
                {
#if DEBUG
                    Logger.Log("malformed media segment: " + url + Logger.Display(e));
#endif
                }

                bool firstDownloadSucceded = await TryDownloadBuffer(mediaSegmentUrl);

                if (firstDownloadSucceded && isFirstSegment)
                {
                    var bufferedRange = sb.Buffered;

                    TimeSpan startingTimestamp = bufferedRange.First().Start;
                    TimeSpan endingTimestamp   = bufferedRange.Last().End;

                    if (sb.Buffered.Count != 0)
                    {
                        sb.Remove(TimeSpan.Zero, endingTimestamp);
#if DEBUG
                        Logger.Log("Removing buffered Range from 0 to " + endingTimestamp);
#endif
                    }
                    sb.TimestampOffset = -startingTimestamp;
#if DEBUG
                    Logger.Log("Setting Timestamp to " + sb.TimestampOffset);
#endif

                    sb.AppendBuffer(activeDownload.Result);
                    isFirstSegment = false;
                }
                else if (!firstDownloadSucceded)
                {
                    bool searchSucceeded = await SearchForSegment();

                    if (!searchSucceeded)
                    {
#if DEBUG
                        Logger.Log("Calling end of stream");
#endif
                        // Failed - should send EndOfStream
                        sb.Abort();
                        isEndOfStream = true;
                    }
                }
            }
            catch (Exception e)
            {
#if DEBUG
                Logger.Log("Error when sending media segment: " + e.Message);
#endif
            }
        }