/// <summary> /// Sends all of the frames this stream needs to send by the given time boundary. /// Also calls populate on the underlying buffer as necessary /// </summary> /// <returns>the time of the next frame to be sent, in ticks</returns> public long OnWakeUp(long timeBoundary) { if (this.currentStreamEnded) { return(long.MaxValue); } #if TIMING long startTimer, takenTime; #endif // We must return the time until the next frame. Keep up with it via this variable. long timeUntilFrame = 0; // Allowing the rtpSender to be created late allows the playback to properly replicate the situation seen // during recording. "Late joiner" support, if you will. This also deals with the underlying 500ms delay // implanted into RtpSession.CreateRtpSender. if (rtpSender != null) { #if TIMING startTimer = DateTime.Now.Ticks; #endif #region Send Frames if (!activeBuffer.Populating && !activeBuffer.Empty) { if (this.outOfDataLoggedFlag) { this.outOfDataLoggedFlag = false; } totalFramesSent += activeBuffer.SendFrames(rtpSender, timeBoundary, out timeUntilFrame, ref totalBytesSent, ref totalLateness); } #endregion #if TIMING takenTime = DateTime.Now.Ticks - startTimer; if (takenTime > Constants.TicksSpent) { Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "TIMING: TIME WASTED SENDING FRAMES: {0} ms", (takenTime / Constants.TicksPerMs))); } startTimer = DateTime.Now.Ticks; #endif #region Empty Buffer if (activeBuffer.Empty) { if (!activeBuffer.EndOfStream) // not the end of the stream, so we go get new data to play out { // guaranteed atomic BufferPlayer oldBuffer = activeBuffer; // Get the next buffer int newBufferIndex = (activeBufferIndex + 1) % Constants.BuffersPerStream; BufferPlayer newBuffer = buffers[newBufferIndex]; if (!newBuffer.Populating) // we can't enable population on the old buffer until we can get the 'LastTick' from the new buffer { if (!newBuffer.EndOfStream) // if we're at the end of the stream, don't enable population on the old buffer { // Broken here so we only work with 2 buffers oldBuffer.EnablePopulation(newBuffer.LastTick + 1); } } else // we're in a performance rut and have run out of data { // the "outOfDataLoggedFlag" prevents us from event-logging too much if (!outOfDataLoggedFlag) { // The database can't keep up; both buffers are empty! eventLog.WriteEntry(string.Format(CultureInfo.CurrentCulture, Strings.OnWakeUpNoDataError, streamID), EventLogEntryType.Warning, ArchiveServiceEventLog.ID.EmptyBuffersInPlayback); this.emptyErrors++; this.outOfDataLoggedFlag = true; } } timeUntilFrame = 0; // we need to come back quick to see when the first frame is in the next buffer // save the "new" buffer as the "current" one activeBufferIndex = newBufferIndex; activeBuffer = buffers[newBufferIndex]; } else // end of stream { if (!currentStreamEnded) { Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "End of stream reached. Stopping sending this stream. ID: {0}", streamID)); currentStreamEnded = true; DisposeSender(); ThreadPool.QueueUserWorkItem(new WaitCallback(FireEndOfStream), this); } timeUntilFrame = long.MaxValue; } } #endregion #if TIMING takenTime = DateTime.Now.Ticks - startTimer; if (takenTime > Constants.TicksSpent) { Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "TIMING: TIME WASTED ON EMPTY BUFFER: {0} ms", (takenTime / Constants.TicksPerMs))); } #endif } else // Sender isn't created yet. Check if we need to. { #if TIMING startTimer = DateTime.Now.Ticks; #endif // Pri2: Change this to be compatable with use of the "playback speed" feature. TimeBoundary is speed-based... timeUntilFrame = (firstStreamTicks - timeBoundary); #region Sender creation if (timeUntilFrame <= Constants.SenderCreationLeadTime) // <x> ms of "prep time" to get fired up { if (!createSenderFired) { createSenderFired = true; Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "RtpSender being created for stream: {0}", streamID)); ThreadPool.QueueUserWorkItem(new WaitCallback(CreateSender)); } } else { timeUntilFrame -= Constants.SenderCreationLeadTime; } #endregion #if TIMING takenTime = DateTime.Now.Ticks - startTimer; if (takenTime > Constants.TicksSpent) { Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "TIMING: TIME WASTED CREATING SENDER: {0} ms", (takenTime / Constants.TicksPerMs))); } #endif } return(timeUntilFrame); }