/// <summary>
        /// In-thread handler for media file opening
        /// </summary>
        /// <param name="stream"></param>
        private void DoNextStream(EncryptedStream stream)
        {
            try
            {
                if (stream == null)
                {
                    if (_state == State.Opening)
                        RaiseError("unable to open stream");

                    if (_demux != null)
                        _demux.Flush(true);

                    _audioBuffer.EndOfPlayback = true;
                    _videoBuffer.EndOfPlayback = true;
                    ReportPendingSamples();

                    return;
                }

                UpdateDemux(stream);

                if (_state == State.Opening)
                {
                    _isBuffering = true;

                    Dictionary<MediaSourceAttributesKeys, string> sourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>();
                    TimeSpan playlistDuration;

                    if (_playback.IsEndList)
                    {
                        playlistDuration = _playback.Duration;
                        sourceAttributes[MediaSourceAttributesKeys.CanSeek] = Boolean.TrueString;
                    }
                    else
                    {
                        if (_playback.Duration > _liveDvrMinDuration)
                        {
                            playlistDuration = _playback.Duration;
                            sourceAttributes[MediaSourceAttributesKeys.CanSeek] = Boolean.TrueString;
                        }
                        else
                        {
                            playlistDuration = new TimeSpan(0, 0, 0);
                            sourceAttributes[MediaSourceAttributesKeys.CanSeek] = Boolean.FalseString;
                        }
                        if (_playback.TargetDuration.TotalSeconds > 0 && _bwHistory.MaxHistoryCount < _playback.TargetDuration.TotalSeconds + 1)
                        {
                            _bwHistory.MaxHistoryCount = (int)_playback.TargetDuration.TotalSeconds + 1;
                        }

                    }

                    if (null != _playlistOverrideEvent)
                    {
                        // Playlist is overrided use max timeline plus duration as whole duration
                        sourceAttributes[MediaSourceAttributesKeys.Duration] = (playlistDuration + TimeSpan.FromTicks(_audioBuffer.MaxStartTimeInAllTimelines)).Ticks.ToString();
                    }
                    else
                    {
                        sourceAttributes[MediaSourceAttributesKeys.Duration] = playlistDuration.Ticks.ToString();
                    }

                    List<MediaStreamDescription> availableMediaStreams = new List<MediaStreamDescription>();
                    availableMediaStreams.Add(_audioBuffer.Description);

                    // The video width and height that we pass to MediaElement should be set to the maximum resolution that
                    // this HLS playlist could possibly use. This ensures that the codec can handle high resolution video
                    // streams as well. The _videoBuffer.Description currently holds the resolution for the current playlist,
                    // which is not necessairly the maximum resolution. If the resolution tag is missing for one of variants,
                    // we will default to the maximim resolution of 1280x720.
                    MediaStreamDescription videoMSD = _videoBuffer.Description;

                    uint maxPicWidth = uint.Parse(videoMSD.MediaAttributes[MediaStreamAttributeKeys.Width]);
                    uint maxPicHeight = uint.Parse(videoMSD.MediaAttributes[MediaStreamAttributeKeys.Height]);

                    foreach (HLSVariant subPlaylist in this.Playback.Program.Variants)
                    {
                        string s;
                        if (subPlaylist.MetaData != null &&
                            subPlaylist.MetaData.TryGetValue(HLSPlaylistMetaKeys.Resolution, out s))
                        {
                            string[] components = s.Split(new char[] { 'x' });
                            if (components != null && components.Length == 2)
                            {
                                uint subStreamWidth = uint.Parse(components[0]);
                                uint subStreamHeight = uint.Parse(components[1]);

                                if (maxPicHeight < subStreamHeight)
                                {
                                    maxPicHeight = subStreamHeight;
                                }

                                if (maxPicWidth < subStreamWidth)
                                {
                                    maxPicWidth = subStreamWidth;
                                }
                            }
                        }
                    }

                    if (_openParam.maxPicWidth > 0)
                    {
                        if (maxPicWidth < _openParam.maxPicWidth)
                        {
                            maxPicWidth = _openParam.maxPicWidth;
                        }
                    }

                    if (_openParam.maxPicHeight > 0)
                    {
                        if (maxPicHeight < _openParam.maxPicHeight)
                        {
                            maxPicHeight = _openParam.maxPicHeight;
                        }
                    }

                    videoMSD.MediaAttributes[MediaStreamAttributeKeys.Width] = maxPicWidth.ToString();
                    videoMSD.MediaAttributes[MediaStreamAttributeKeys.Height] = maxPicHeight.ToString();

                    availableMediaStreams.Add(videoMSD);
                    ReportOpenMediaCompleted(sourceAttributes, availableMediaStreams);

                    _state = State.Opened;
                }
            }
            catch (Exception exception)
            {
                RaiseError(exception);
            }
            finally
            {
                _playback.EndPendingWebRequest();
            }
        }
        /// <summary>
        /// Create new demux from a stream
        /// </summary>
        /// <param name="stream"></param>        
        private void UpdateDemux(EncryptedStream stream)
        {
            TSDemux newDemux;
            if (_demux != null && _demux.IsMediaInfoReady )
            {
                newDemux = new TSDemux(stream, _demux, _playback.Metadata, _playback.CurrentDownloadBitrate, _bwHistory);
            }
            else
            {
                newDemux = new TSDemux(stream, _audioBuffer, _videoBuffer, _playback.Metadata, _playback.CurrentDownloadBitrate, _bwHistory);
            }

            if (_demux != null)
            {
                if (!_demux.IsMediaInfoReady)
                {
                    // If last demux has no media info ready, we are not carrying over demux parser buffers has been allocated,
                    // so make sure we discard those buffers by Flush(true)
                    _demux.Flush(true);
                    HLSTrace.WriteLine("Discard samples as last demux has no media info ready");
                }
                _demux.Dispose();
            }

            _demux = newDemux;

            while (!_demux.IsMediaInfoReady && !_demux.IsEndOfStream)
            {
                _demux.ReadChunk();
            }

            if (!_demux.IsMediaInfoReady)
                RaiseError("unable to parse stream");
        }
Example #3
0
        /// <summary>
        /// Common construction code for all constructors
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="audioBuffer"></param>
        /// <param name="videoBuffer"></param>
        private void CommonConstruct(EncryptedStream stream, SampleBuffer audioBuffer,
                                    SampleBuffer videoBuffer, IContainerMetadata metadata, uint bitrate,
                                    BandwidthHistory BWHistory)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            _stream = stream;
            _metadata = metadata;
            _audioBuffer = audioBuffer;
            _audioBuffer.ResetOnSegmentStart();
            _videoBuffer = videoBuffer;
            _videoBuffer.ResetOnSegmentStart();

            _previousReadEndTime = stream.RequestStartTime;

            _bitrate = bitrate;

            _downloadChunkBuffer = new byte[_downloadChunkSize];
            _TSPacketBuffer = new byte[TSPacketSize];

            _BWHistory = BWHistory;
        }
Example #4
0
 /// <summary>
 /// Implements Dispose logic
 /// </summary>
 /// <param name="disposing"></param>
 protected virtual void Dispose(bool disposing)
 {
     if (!_disposed)
     {
         if (disposing)
         {
             if (_stream != null)
             {
                 _stream.Dispose();
                 _stream = null;
             }
         }
         _disposed = true;
     }
 }
Example #5
0
        /// <summary>
        /// Special constructor for creating new demux for next stream in sequence
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="previousDemux"></param>
        public TSDemux(EncryptedStream stream, TSDemux previousDemux, IContainerMetadata metadata, uint bitrate, BandwidthHistory BWHistory)
        {
            if (previousDemux == null)
                throw new ArgumentNullException("previousDemux");

            CommonConstruct(stream, previousDemux._audioBuffer, previousDemux._videoBuffer, metadata, bitrate, BWHistory);

            if (!stream.Discontinuity)
                _streams = previousDemux._streams;

            _audioFormatParser = previousDemux._audioFormatParser;
            _videoFormatParser = previousDemux._videoFormatParser;
            _audioFormatParser.HLSStream = stream.HLSStream;
            _videoFormatParser.HLSStream = stream.HLSStream;
        }
Example #6
0
 /// <summary>
 ///  Default constructor.
 /// </summary>
 /// <param name="stream"></param>
 /// <param name="audioBuffer"></param>
 /// <param name="videoBuffer"></param>
 public TSDemux(EncryptedStream stream, SampleBuffer audioBuffer,
                SampleBuffer videoBuffer, IContainerMetadata metadata, uint bitrate, 
                BandwidthHistory BWHistory)
 {
     CommonConstruct(stream, audioBuffer, videoBuffer, metadata, bitrate, BWHistory);
 }
Example #7
0
        /// <summary>
        /// Asynchronous callback for handling stream data
        /// </summary>
        /// <param name="asyncResult"></param>
        private void DataStreamResponseReceived(IAsyncResult asyncResult)
        {
            lock (_requestLock)
            {
                WebRequestState requestState = null;
                HttpWebResponse response = null;

                try
                {
                    Debug.Assert(asyncResult.AsyncState is WebRequestState, "asyncResult.AsyncState must be of type WebRequestState");

                    requestState = (WebRequestState)(asyncResult.AsyncState);

                    if (requestState.DiscardData)
                    {
                        return;
                    }

                    response = (HttpWebResponse)requestState.WebRequest.EndGetResponse(asyncResult);
                    HLSTrace.TestInjectRandomError( "DataStreamResponseReceived", 0.1f );
                    HLSTrace.WriteLine("Downloaded response status {0} for {1}", response.StatusDescription, requestState.WebRequest.RequestUri.ToString());

                    if (requestState.DiscardData || requestState.AsyncResult.IsAborted)
                    {
                        requestState.AsyncResult.Dispose();

                        if (response.GetResponseStream() != null)
                            response.GetResponseStream().Close();

                        response.Close();
                        response = null;
                        return;
                    }

                    if (response.GetResponseStream() != null)
                    {
                        EncryptedStream playbackStream = null;
                        bool discontinuity = _discontinuity || CurrentStream.Discontinuity;
                        if (CurrentStream.EncryptionMethod == HLSEncryptionMethod.AES128)
                        {
                            using (AesManaged aes = new AesManaged())
                            {
                                aes.Key = _playlist.GetEncryptionKeyForStream(CurrentStream);
                                aes.IV = SynthesizeInitializationVector();
                                playbackStream = new EncryptedStream(response.GetResponseStream(), requestState.StartTime, response, discontinuity, aes.CreateDecryptor(), TSDemux.TSPacketSize, CurrentStream);
                            }
                        }
                        else
                        {
                            playbackStream = new EncryptedStream(response.GetResponseStream(), requestState.StartTime, response, discontinuity, CurrentStream);
                        }

                        Debug.Assert(_asyncResult != null, "_asyncResult cannot be null");
                        _asyncResult.CompleteWithAsyncState(playbackStream);

                    }
                    else
                    {
                        _asyncResult.CompleteWithAsyncState(null);
                    }
                }
                catch (Exception e)
                {
                    HLSTrace.PrintException(e);
                    if (response != null)
                    {
                        response.Close();
                    }

                    if (requestState.DiscardData || requestState.AsyncResult.IsAborted)
                    {
                        requestState.AsyncResult.Dispose();

                        return;
                    }

                    if (_reTryCount < MAX_NUMBER_OF_RETRIES)
                    {
                        HLSTrace.WriteLine( " download retrying...  count={0}", _reTryCount );
                        BeginLoadingNextStream();
                        _reTryCount++;
                    }
                    else
                    {
                        lock (this)
                        {
                            HLSTrace.WriteLine(" download retries all failed, skip it and download next chunk ...  count={0}", _reTryCount);
                            _reTryCount = 0;
                            _currentPosition++;
                            _currentMediaSequenceNumber++;
                            _discontinuity = true;
                            if (!IsEndList && (_currentPosition >= _playlist.Streams.Count))
                            {
                                _asyncResult.Purpose = HLSCallbackPurpose.WaitForPlaylist;
                                _playlist.Reload();
                                return;
                            }

                            BeginLoadingNextStream();
                        }
                    }
                }
            }
        }