コード例 #1
0
        public bool ChangeStream(StreamDescription stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream), "stream cannot be null");
            }

            if (availableStreams.Count <= stream.Id)
            {
                throw new ArgumentOutOfRangeException();
            }

            var newMedia          = availableStreams[stream.Id].Media;
            var newRepresentation = availableStreams[stream.Id].Representation;

            var newStream = new DashStream(newMedia, newRepresentation);

            if (currentStream.Media.Type.Value != newMedia.Type.Value)
            {
                throw new ArgumentException("wrong media type");
            }

            if (newStream.Equals(currentStream))
            {
                Logger.Info($"Selected stream {stream.Id} {stream.Description} already playing. Not changing.");
                return(false);
            }

            SetStream(newStream);
            Logger.Info($"Stream {stream.Id} {stream.Description} set.");
            return(true);
        }
コード例 #2
0
        private void StartPipeline(DashStream newStream = null)
        {
            if (pipelineStarted)
            {
                return;
            }

            if (newStream != null)
            {
                currentStream = newStream;

                Logger.Info($"{StreamType}: Dash pipeline start.");
                Logger.Info($"{StreamType}: Media: {currentStream.Media}");
                Logger.Info($"{StreamType}: {currentStream.Representation}");

                dashClient.UpdateRepresentation(currentStream.Representation);
                ParseDrms(currentStream.Media);
                PushMetaDataConfiguration();
            }

            var fullInitRequired = (newStream != null) || DisableAdaptiveStreaming;

            demuxerController.StartForEs();
            dashClient.Start(fullInitRequired);

            pipelineStarted = true;
        }
コード例 #3
0
        public void AdaptToNetConditions()
        {
            // Treat adaptive streaming as "non critical". If cannot be processed now due to lock, attempt
            // will be done at next iteration.
            if (!Monitor.TryEnter(switchStreamLock))
            {
                return;
            }

            try
            {
                if (DisableAdaptiveStreaming)
                {
                    return;
                }

                if (currentStream == null && pendingStream == null)
                {
                    return;
                }

                var streamToAdapt = pendingStream ?? currentStream;
                if (streamToAdapt.Representation.Bandwidth.HasValue == false)
                {
                    return;
                }

                var currentThroughput = throughputHistory.GetAverageThroughput();
                if (Math.Abs(currentThroughput) < 0.1)
                {
                    return;
                }

                Logger.Debug("Adaptation values:");
                Logger.Debug("  current throughput: " + currentThroughput);
                Logger.Debug("  current stream bandwidth: " + streamToAdapt.Representation.Bandwidth.Value);

                // availableStreams is sorted array by descending bandwidth
                var stream = availableStreams.FirstOrDefault(o =>
                                                             o.Representation.Bandwidth <= currentThroughput) ?? availableStreams.Last();

                if (stream.Representation.Bandwidth == streamToAdapt.Representation.Bandwidth)
                {
                    return;
                }

                Logger.Info("Changing stream to bandwidth: " + stream.Representation.Bandwidth);
                pendingStream = stream;
            }
            finally
            {
                Monitor.Exit(switchStreamLock);
            }
        }
コード例 #4
0
        public void Stop()
        {
            if (!pipelineStarted)
            {
                return;
            }

            dashClient.Stop();

            ResetPipeline();

            currentStream = null;
            pendingStream = null;
        }
コード例 #5
0
        public async Task SwitchStreamIfNeeded()
        {
            // Access serialization is needed.
            // SwitchStreamIfNeeded can be called from DashDataProvider or
            // timer based manifest reload (different threads) which can cause
            // null object reference if pendingStream is nulled AFTER another thread
            // passed pendingStream null check.
            //
            // Stream switching does not need to be serialized. If stream switch is already
            // in progress, next stream switch can safely be ignored, thus use of monitor
            // rather then lock.

            if (!Monitor.TryEnter(switchStreamLock))
            {
                return;
            }

            try
            {
                if (pendingStream == null)
                {
                    return;
                }

                Logger.Info($"{StreamType}");

                if (currentStream == null)
                {
                    StartPipeline(pendingStream);
                    pendingStream = null;
                    return;
                }

                if (!CanSwitchStream())
                {
                    return;
                }

                await FlushPipeline();

                StartPipeline(pendingStream);

                pendingStream = null;
            }
            finally
            {
                Monitor.Exit(switchStreamLock);
            }
        }
コード例 #6
0
 private void SetStream(DashStream newStream)
 {
     Logger.Info("");
     Monitor.Enter(switchStreamLock);
     try
     {
         DisableAdaptiveStreaming = true;
         currentStream            = newStream;
         pendingStream            = null;
         dashClient.UpdateRepresentation(currentStream.Representation);
     }
     finally
     {
         Monitor.Exit(switchStreamLock);
     }
 }
コード例 #7
0
        private void SetStream(DashStream newStream)
        {
            Logger.Info("");
            Monitor.Enter(switchStreamLock);
            try
            {
                ResetPipeline();
                DisableAdaptiveStreaming = true;
                pendingStream            = null;

                StartPipeline(newStream);
            }
            finally
            {
                Monitor.Exit(switchStreamLock);
            }
        }
コード例 #8
0
        private static string CreateStreamDescription(DashStream stream)
        {
            var description = "";

            if (!string.IsNullOrEmpty(stream.Media.Lang))
            {
                description += stream.Media.Lang;
            }
            if (stream.Representation.Height.HasValue && stream.Representation.Width.HasValue)
            {
                description += $" ( {stream.Representation.Width}x{stream.Representation.Height} )";
            }
            if (stream.Representation.NumChannels.HasValue)
            {
                description += $" ( {stream.Representation.NumChannels} ch )";
            }

            return(description);
        }
コード例 #9
0
        public void Stop()
        {
            if (!pipelineStarted)
            {
                return;
            }

            dashClient.Stop();

            ResetPipeline();

            demuxerClock.Reset();
            lastPushedClock.Reset();
            lastSeek = TimeSpan.Zero;

            trimOffset    = null;
            currentStream = null;
            pendingStream = null;
        }
コード例 #10
0
        public void ChangeStream(StreamDescription stream)
        {
            Logger.Info("");

            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream), "stream cannot be null");
            }

            if (availableStreams.Count <= stream.Id)
            {
                throw new ArgumentOutOfRangeException();
            }

            var newMedia          = availableStreams[stream.Id].Media;
            var newRepresentation = availableStreams[stream.Id].Representation;

            // Share lock with switchStreamIfNeeded. Change stream may happen on a separate thread.
            // As such, we do not want 2 starts happening
            // - One as a manual stream selection
            // - One as a adaptive stream switching.
            //
            lock (switchStreamLock)
            {
                var newStream = new DashStream(newMedia, newRepresentation);

                if (currentStream.Media.Type.Value != newMedia.Type.Value)
                {
                    throw new ArgumentException("wrong media type");
                }

                DisableAdaptiveStreaming = true;

                FlushPipeline().ContinueWith(task =>
                {
                    if (task.Status == TaskStatus.RanToCompletion)
                    {
                        StartPipeline(newStream);
                    }
                },
                                             TaskScheduler.FromCurrentSynchronizationContext());
            }
        }
コード例 #11
0
        public void AdaptToNetConditions()
        {
            if (DisableAdaptiveStreaming)
            {
                return;
            }

            if (currentStream == null && pendingStream == null)
            {
                return;
            }

            var streamToAdapt = pendingStream ?? currentStream;

            if (streamToAdapt.Representation.Bandwidth.HasValue == false)
            {
                return;
            }

            var currentThroughput = throughputHistory.GetAverageThroughput();

            if (Math.Abs(currentThroughput) < 0.1)
            {
                return;
            }

            Logger.Debug("Adaptation values:");
            Logger.Debug("  current throughput: " + currentThroughput);
            Logger.Debug("  current stream bandwidth: " + streamToAdapt.Representation.Bandwidth.Value);

            // availableStreams is sorted array by descending bandwidth
            var stream = availableStreams.FirstOrDefault(o =>
                                                         o.Representation.Bandwidth <= currentThroughput) ?? availableStreams.Last();

            if (stream.Representation.Bandwidth == streamToAdapt.Representation.Bandwidth)
            {
                return;
            }

            Logger.Info("Changing stream to bandwidth: " + stream.Representation.Bandwidth);
            pendingStream = stream;
        }
コード例 #12
0
        public void UpdateMedia(Period period)
        {
            var media = period.GetAdaptationSets(ToMediaType(StreamType));

            if (!media.Any())
            {
                throw new ArgumentOutOfRangeException($"{StreamType}: No media in period {period}");
            }

            lock (switchStreamLock)
            {
                if (currentStream != null)
                {
                    var currentMedia = media.Count == 1
                        ? media.First()
                        : media.FirstOrDefault(o => o.Id == currentStream.Media.Id);
                    var currentRepresentation =
                        currentMedia?.Representations.FirstOrDefault(o => o.Id == currentStream.Representation.Id);
                    if (currentRepresentation != null)
                    {
                        GetAvailableStreams(media, currentMedia);

                        // Media Preparation (Call to Initialize) is done upon assignment to pendingStream.
                        currentStream = new DashStream(currentMedia, currentRepresentation);
                        dashClient.UpdateRepresentation(currentStream.Representation);

                        PushMetaDataConfiguration();

                        return;
                    }
                }

                var defaultMedia = GetDefaultMedia(media);
                GetAvailableStreams(media, defaultMedia);
                // get first element of sorted array
                var representation = defaultMedia.Representations.OrderByDescending(o => o.Bandwidth).Last();

                pendingStream = new DashStream(defaultMedia, representation);
            }
        }
コード例 #13
0
        private void StartPipeline(DashStream newStream = null)
        {
            if (pipelineStarted)
            {
                return;
            }

            if (newStream != null)
            {
                currentStream = newStream;

                Logger.Info($"{StreamType}: Dash pipeline start.");
                Logger.Info($"{StreamType}: Media: {currentStream.Media}");
                Logger.Info($"{StreamType}: {currentStream.Representation}");

                dashClient.UpdateRepresentation(currentStream.Representation);
                ParseDrms(currentStream.Media);
            }

            if (!trimOffset.HasValue)
            {
                trimOffset = currentStream.Representation.AlignedTrimOffset;
            }

            var fullInitRequired = (newStream != null);

            demuxerController.StartForEs();
            dashClient.Start(fullInitRequired);

            pipelineStarted = true;

            if (newStream != null)
            {
                PushMetaDataConfiguration();
            }
        }