/// <summary> /// [NatNet] m_NatNet_OnFrameReady will be called when a frame of Mocap /// data has is received from the server application. /// /// Note: This callback is on the network service thread, so it is /// important to return from this function quickly as possible /// to prevent incoming frames of data from buffering up on the /// network socket. /// /// Note: "data" is a reference structure to the current frame of data. /// NatNet re-uses this same instance for each incoming frame, so it should /// not be kept (the values contained in "data" will become replaced after /// this callback function has exited). /// </summary> /// <param name="data">The actual frame of mocap data</param> /// <param name="client">The NatNet client instance</param> void m_NatNet_OnFrameReady(NatNetML.FrameOfMocapData data, NatNetML.NatNetClientML client) { double elapsedIntraMS = 0.0f; QueryPerfCounter intraTimer = new QueryPerfCounter(); intraTimer.Start(); // detect and report and 'measured' frame drop (as measured by client) m_FramePeriodTimer.Stop(); double elapsedMS = m_FramePeriodTimer.Duration(); ProcessFrameOfData(ref data); // report if we are taking too long, which blocks packet receiving, which if long enough would result in socket buffer drop intraTimer.Stop(); elapsedIntraMS = intraTimer.Duration(); if (elapsedIntraMS > 5.0f) { OutputMessage("Warning : Frame handler taking too long: " + elapsedIntraMS.ToString("F2")); } m_FramePeriodTimer.Start(); }
/// <summary> /// Refresh the UI at a fixed period specified by the timer /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UpdateUITimer_Tick(object sender, EventArgs e) { m_UIUpdateTimer.Stop(); double interframeDuration = m_UIUpdateTimer.Duration(); QueryPerfCounter uiIntraFrameTimer = new QueryPerfCounter(); uiIntraFrameTimer.Start(); // the frame queue is a shared resource with the FrameOfMocap delivery thread, so lock it while reading // note this can block the frame delivery thread. In a production application frame queue management would be optimized. lock (syncLock) { while (m_FrameQueue.Count > 0) { m_FrameOfData = m_FrameQueue.Dequeue(); if (m_FrameQueue.Count > 0) { continue; } if (m_FrameOfData != null) { // for servers that only use timestamps, not frame numbers, calculate a // frame number from the time delta between frames if (desc.HostApp.Contains("TrackingTools")) { m_fCurrentMocapFrameTimestamp = m_FrameOfData.fLatency; if (m_fCurrentMocapFrameTimestamp == m_fLastFrameTimestamp) { continue; } if (m_fFirstMocapFrameTimestamp == 0.0f) { m_fFirstMocapFrameTimestamp = m_fCurrentMocapFrameTimestamp; } m_FrameOfData.iFrame = (int)((m_fCurrentMocapFrameTimestamp - m_fFirstMocapFrameTimestamp) * m_ServerFramerate); } // update the data grid UpdateDataGrid(); // update the chart UpdateChart(m_FrameOfData.iFrame); // only redraw chart when necessary, not for every frame if (m_FrameQueue.Count == 0) { chart1.ChartAreas[0].RecalculateAxesScale(); chart1.ChartAreas[0].AxisX.Minimum = 0; chart1.ChartAreas[0].AxisX.Maximum = GraphFrames; chart1.Invalidate(); } // Mocap server timestamp (in seconds) m_fLastFrameTimestamp = m_FrameOfData.fTimestamp; TimestampValue.Text = m_FrameOfData.fTimestamp.ToString("F3"); // SMPTE timecode (if timecode generator present) int hour, minute, second, frame, subframe; bool bSuccess = m_NatNet.DecodeTimecode(m_FrameOfData.Timecode, m_FrameOfData.TimecodeSubframe, out hour, out minute, out second, out frame, out subframe); if (bSuccess) { TimecodeValue.Text = string.Format("{0:D2}:{1:D2}:{2:D2}:{3:D2}.{4:D2}", hour, minute, second, frame, subframe); } if (m_FrameOfData.bRecording) { chart1.BackColor = Color.Red; } else { chart1.BackColor = Color.White; } } } } uiIntraFrameTimer.Stop(); double uiIntraFrameDuration = uiIntraFrameTimer.Duration(); m_UIUpdateTimer.Start(); }
/// <summary> /// Refresh the UI at a fixed period specified by the timer /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UpdateUITimer_Tick(object sender, EventArgs e) { m_UIUpdateTimer.Stop(); double interframeDuration = m_UIUpdateTimer.Duration(); QueryPerfCounter uiIntraFrameTimer = new QueryPerfCounter(); uiIntraFrameTimer.Start(); // the frame queue is a shared resource with the FrameOfMocap delivery thread, so lock it while reading // note this can block the frame delivery thread. In a production application frame queue management would be optimized. lock (syncLock) { while (m_FrameQueue.Count > 0) { m_FrameOfData = m_FrameQueue.Dequeue(); if (m_FrameQueue.Count > 0) continue; if (m_FrameOfData != null) { // for servers that only use timestamps, not frame numbers, calculate a // frame number from the time delta between frames if (desc.HostApp.Contains("TrackingTools")) { m_fCurrentMocapFrameTimestamp = m_FrameOfData.fLatency; if (m_fCurrentMocapFrameTimestamp == m_fLastFrameTimestamp) { continue; } if (m_fFirstMocapFrameTimestamp == 0.0f) { m_fFirstMocapFrameTimestamp = m_fCurrentMocapFrameTimestamp; } m_FrameOfData.iFrame = (int)((m_fCurrentMocapFrameTimestamp - m_fFirstMocapFrameTimestamp) * m_ServerFramerate); } // update the data grid UpdateDataGrid(); // update the chart UpdateChart(m_FrameOfData.iFrame); // only redraw chart when necessary, not for every frame if (m_FrameQueue.Count == 0) { chart1.ChartAreas[0].RecalculateAxesScale(); chart1.ChartAreas[0].AxisX.Minimum = 0; chart1.ChartAreas[0].AxisX.Maximum = GraphFrames; chart1.Invalidate(); } // Mocap server timestamp (in seconds) m_fLastFrameTimestamp = m_FrameOfData.fTimestamp; TimestampValue.Text = m_FrameOfData.fTimestamp.ToString("F3"); // SMPTE timecode (if timecode generator present) int hour, minute, second, frame, subframe; bool bSuccess = m_NatNet.DecodeTimecode(m_FrameOfData.Timecode, m_FrameOfData.TimecodeSubframe, out hour, out minute, out second, out frame, out subframe); if (bSuccess) TimecodeValue.Text = string.Format("{0:D2}:{1:D2}:{2:D2}:{3:D2}.{4:D2}", hour, minute, second, frame, subframe); if (m_FrameOfData.bRecording) chart1.BackColor = Color.Red; else chart1.BackColor = Color.White; } } } uiIntraFrameTimer.Stop(); double uiIntraFrameDuration = uiIntraFrameTimer.Duration(); m_UIUpdateTimer.Start(); }
/// <summary> /// [NatNet] m_NatNet_OnFrameReady will be called when a frame of Mocap /// data has is received from the server application. /// /// Note: This callback is on the network service thread, so it is /// important to return from this function quickly as possible /// to prevent incoming frames of data from buffering up on the /// network socket. /// /// Note: "data" is a reference structure to the current frame of data. /// NatNet re-uses this same instance for each incoming frame, so it should /// not be kept (the values contained in "data" will become replaced after /// this callback function has exited). /// </summary> /// <param name="data">The actual frame of mocap data</param> /// <param name="client">The NatNet client instance</param> void m_NatNet_OnFrameReady(NatNetML.FrameOfMocapData data, NatNetML.NatNetClientML client) { double elapsedIntraMS = 0.0f; QueryPerfCounter intraTimer = new QueryPerfCounter(); intraTimer.Start(); // check and report frame arrival period (time elapsed since previous frame arrived) m_FramePeriodTimer.Stop(); double elapsedMS = m_FramePeriodTimer.Duration(); if ( (mLastFrame % 100) == 0) { OutputMessage("FrameID:" + data.iFrame + " Timestamp: " + data.fTimestamp + " Period:" + elapsedMS); } // check and report frame drop if ((mLastFrame != 0) && ((data.iFrame - mLastFrame) != 1)) { OutputMessage("Frame Drop: ( ThisFrame: " + data.iFrame.ToString() + " LastFrame: " + mLastFrame.ToString() + " )"); } // [NatNet] Add the incoming frame of mocap data to our frame queue, // Note: the frame queue is a shared resource with the UI thread, so lock it while writing lock (syncLock) { // [optional] clear the frame queue before adding a new frame m_FrameQueue.Clear(); FrameOfMocapData deepCopy = new FrameOfMocapData(data); m_FrameQueue.Enqueue(deepCopy); } intraTimer.Stop(); elapsedIntraMS = intraTimer.Duration(); if (elapsedIntraMS > 5.0f) { OutputMessage("Warning : Frame handler taking too long: " + elapsedIntraMS.ToString("F2")); } mLastFrame = data.iFrame; m_FramePeriodTimer.Start(); }