Esempio n. 1
0
 /// <summary>
 /// Clear the queue and add 1 item in the same operation. This is useful
 /// for operation that take precedence over all others (like closing and errors)
 /// </summary>
 /// <param name="elem">New item to add</param>
 public void ClearAndEnqueue(WorkQueueElement elem)
 {
     lock (m_queue)
     {
         m_queue.Clear();
         m_queue.Enqueue(elem);
         m_queueHasItemsEvent.Set();
     }
 }
        /// <summary>
        /// Handle a sample request
        /// </summary>
        /// <param name="elem">the work command we are handling</param>
        private void DoSample(WorkQueueElement elem)
        {
            DateTime enter = DateTime.Now;

            // Give the sample if available, nothing otherwise
            MediaStreamType mediaStreamType = (MediaStreamType)elem.CommandParameter;
            int mediaTypeIndex = (int)mediaStreamType;

            // Get the queue of chunks for this stream
            MediaChunkQueue mediaQueue = m_manifestInfo.GetStreamInfoForStreamType(mediaStreamType).Queue;
            int mediaTypeMask = mediaStreamType == MediaStreamType.Video ? 0x01 : 0x02;

            MediaChunk chunk = null;
            GetFrameData frameData = null;

            bool flag = true;
            while (flag)
            {
                // Get the current chunk from our media queue
                flag = false;
                chunk = mediaQueue.Current;
                if (chunk == null)
                {
                    // This is a redudant code in a case native side calls twice after the end of media
                    // It should not happen but because we played with serialization on/off we keep it in place as a defensive code
                    DoReportGetSampleCompleted(null, new MediaStreamSample(m_manifestInfo.GetStreamInfoForStreamType(mediaStreamType).Description, null, 0, 0, 0, m_nullAttributes));
                    return;
                }

                // If we have not finished parsing the chunk yet, then we can't send the sample
                if (chunk.State == MediaChunk.ChunkState.Pending || chunk.State == MediaChunk.ChunkState.Loaded)
                {
                    // If the chunk is pending but hasn't been downloaded yet, then force a download
                    if (chunk.State == MediaChunk.ChunkState.Pending && chunk.Downloader == null)
                    {
                        if (chunk.Bitrate == 0)
                        {
                            // Silverlight failed to load previous chunk or inform us about failure and as a result we did not even started a new one
                            chunk.Bitrate = m_manifestInfo.GetStreamInfoForStreamType(chunk.MediaType).Bitrates[0];
                        }

                        Tracer.Trace(TraceChannel.Error, "Lost {0} in state {1} trying to load again.", chunk.Sid, chunk.State);
                        m_heuristics.ForceNextDownload(chunk.StreamId, chunk.ChunkId);
                        m_heuristics.ScheduleDownloads();
                    }

                    // Media chunk is not yet available, try again later
                    m_workQueue.Enqueue(elem);
                    ReportGetSampleProgress(chunk.DownloadPercent / 100.0f);

                    if ((m_bufferingStateMask & mediaTypeMask) == 0)
                    {
                        if (m_bufferingStateMask == 0)
                        {
                            FireBufferingStarted();
                        }

                        m_bufferingStateMask |= mediaTypeMask;
                    }

                    // Take a nap to give us some time to download. If it's already downloaded,
                    // then it just hasn't been parsed yet
                    if (chunk.DownloadPercent < 100)
                    {
                        Thread.Sleep(10);
                    }

                    if (chunk.SampleRequestsMissed++ == 0)
                    {
                        Tracer.Trace(
                            TraceChannel.Error,
                            "Chunk {0} is not available on sample request, chunk state {1}, downloader is {2}",
                            chunk.Sid,
                            chunk.State,
                            chunk.Downloader == null ? "null" : "not null");
                    }
                    else if (chunk.SampleRequestsMissed % 100 == 0)
                    {
                        Tracer.Trace(
                            TraceChannel.Error,
                            "Chunk {0} is not available for {3} seconds, chunk state {1}, downloader is {2}",
                            chunk.Sid,
                            chunk.State,
                            chunk.Downloader == null ? "null" : "not null",
                            chunk.SampleRequestsMissed / 100);
                    }
                    else if (chunk.SampleRequestsMissed >= (m_playbackInfo.IsPlaying ? 500 : 1500))
                    {
                        // After 5 seconds delay during play or 15 seconds while paused or stopped, move on to the next chunk.
                        if (chunk.Downloader != null)
                        {
                            chunk.Downloader.CancelDownload();
                        }

                        chunk.SampleRequestsMissed = 0;

                        m_consecutiveMissedChunks[mediaTypeIndex]++;
                        string msg = String.Format(CultureInfo.InvariantCulture, "Failed to load in time media chunk {0} ({1},{2}, #{3} in a row)", chunk.Sid, chunk.Bitrate, chunk.State, m_consecutiveMissedChunks[mediaTypeIndex]);

                        // If we have missed to many, then throw an error
                        if (m_consecutiveMissedChunks[mediaTypeIndex] >= Configuration.Playback.MaxMissingOrCorruptedChunks)
                        {
                            throw new AdaptiveStreamingException(msg);
                        }
                        else
                        {
                            Tracer.Trace(TraceChannel.Error, msg);
                        }

                        mediaQueue.MoveNext();

                        // No need to verify flag, if we hit end of stream we'll know in 10 milliseconds
                    }

                    DateTime exit2 = DateTime.Now;
                    if ((exit2 - enter).TotalSeconds > 20e-3)
                    {
                        Tracer.Trace(TraceChannel.Timing, "DoSample: long time: {0}", (exit2 - enter).TotalSeconds);
                    }

                    return;
                }
                else if (chunk.State != MediaChunk.ChunkState.Parsed)
                {
                    // We are not parsed, so flag us as missed
                    m_consecutiveMissedChunks[mediaTypeIndex]++;
                    string msg = String.Format(
                        CultureInfo.InvariantCulture,
                        "Failed to {0} media chunk {1} ({2}), #{3} in a row.",
                        chunk.State == MediaChunk.ChunkState.Error ? "download" : "parse",
                        chunk.Sid,
                        chunk.Bitrate,
                        m_consecutiveMissedChunks[mediaTypeIndex]);

                    if (m_consecutiveMissedChunks[mediaTypeIndex] >= Configuration.Playback.MaxMissingOrCorruptedChunks)
                    {
                        throw new AdaptiveStreamingException(msg);
                    }
                    else
                    {
                        Tracer.Trace(TraceChannel.Error, msg);
                    }

                    mediaQueue.MoveNext();
                    m_workQueue.Enqueue(elem);
                    return;
                }

                // If we get here, then we should have a frame. Try to get it from our parser
                try
                {
                    if ((frameData = chunk.GetNextFrame()) == null)
                    {
                        // We could not get a frame from our parser, so move to the next chunk
                        flag = mediaQueue.MoveNext();
                        if (!flag)
                        {
                            // Signal end of the stream
                            DoReportGetSampleCompleted(null, new MediaStreamSample(m_manifestInfo.GetStreamInfoForStreamType(mediaStreamType).Description, null, 0, 0, 0, m_nullAttributes));
                            return;
                        }
                    }
                }
                catch (ChunkNotParsedException)
                {
                    // We could not get a frame from our parser, so move to the next chunk
                    flag = mediaQueue.MoveNext();
                    if (!flag)
                    {
                        // Signal end of the stream
                        DoReportGetSampleCompleted(null, new MediaStreamSample(m_manifestInfo.GetStreamInfoForStreamType(mediaStreamType).Description, null, 0, 0, 0, m_nullAttributes));
                        return;
                    }
                }
            }

            if (chunk.SampleRequestsMissed > 0)
            {
                Tracer.Trace(TraceChannel.Error, "Chunk {0} was not available for {1} milliseconds", chunk.Sid, chunk.SampleRequestsMissed * 10);
            }

            // Since we have a chunk here, we can reset are missed requests
            chunk.SampleRequestsMissed = 0;
            m_consecutiveMissedChunks[mediaTypeIndex] = 0;

            // Update our buffering state
            if ((m_bufferingStateMask & mediaTypeMask) != 0)
            {
                m_bufferingStateMask &= ~mediaTypeMask;

                if (m_bufferingStateMask == 0)
                {
                    FireBufferingDone();
                }
            }

            // Notify everyone about the bitrate we are using
            FireOnPlayBitrateChange(chunk.MediaType, chunk.Bitrate, DateTime.Now);

            // Check to see if we have any DRM attributes
            Dictionary<MediaSampleAttributeKeys, string> sampleAttributes = new Dictionary<MediaSampleAttributeKeys, string>();
            if (frameData.DrmData != null)
            {
                sampleAttributes.Add(/*"XCP_MS_SAMPLE_DRM"*/ MediaSampleAttributeKeys.DRMInitializationVector, Convert.ToBase64String(frameData.DrmData));
            }

            // Create the sample that we send to the media element
            MediaStreamSample sample = new MediaStreamSample(
                m_manifestInfo.GetStreamInfoForStreamType(mediaStreamType).Description,
                chunk.DownloadedPiece,
                frameData.StartOffset,
                frameData.FrameSize,
                frameData.Timestamp,
                sampleAttributes);

            // Must call if chunk.GetNextFrame is called, which happens only here above
            mediaQueue.UpdateBufferSizes();

            // Report this sample to our heuristics module
            if (mediaStreamType == MediaStreamType.Video)
            {
                m_playbackInfo.SourceFramesPerSecond = chunk.FrameRate;
            }

            m_heuristics.OnSampleDelivered(mediaStreamType, chunk.ChunkId, chunk.Bitrate, frameData.Timestamp);

            // Respond to the media element
            DoReportGetSampleCompleted(chunk, sample);

            DateTime exit = DateTime.Now;
            if ((exit - enter).TotalSeconds > 20e-3)
            {
                Tracer.Trace(TraceChannel.Timing, "DoSample: long time: {0}", (exit - enter).TotalSeconds);
            }
        }
Esempio n. 3
0
 /// <summary>
 /// Enqueue a new work item
 /// </summary>
 /// <param name="elem">the item to add</param>
 public void Enqueue(WorkQueueElement elem)
 {
     lock (m_queue)
     {
         m_queue.Enqueue(elem);
         if (1 == m_queue.Count)
         {
             m_queueHasItemsEvent.Set();
         }
     }
 }