//UI更新线程 private void UpdateUI() { m_UIUpdateTimer.Stop(); //UI数据时间更新停止 double interframeDuration = m_UIUpdateTimer.Duration(); //定义持续时间 QueryPerfCounter uiIntraFrameTimer = new QueryPerfCounter(); uiIntraFrameTimer.Start(); //UI数据时间更新开始 // 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. //帧队列是一个共享资源的frameofmocap输送线,所以把它锁在阅读 //注意,这可以阻止帧传递线程。在生产应用程序框架中,队列管理将得到优化。 //当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。 //但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,我们要使该资源在同一时刻只能被一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性。lock是C#中最常用的同步方式 lock (syncLock) { while (m_FrameQueue.Count > 0) //如果帧队列数据个数大于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(); //更新数据 // Mocap server timestamp (in seconds) //m_fLastFrameTimestamp = m_FrameOfData.fTimestamp; TimestampValue.Text = m_FrameOfData.fTimestamp.ToString("F3"); //帧数据中的时间赋值给TimestampValue DroppedFrameCountLabel.Text = mDroppedFrames.ToString(); //丢帧 } } } uiIntraFrameTimer.Stop(); //UI帧数据时间停止 double uiIntraFrameDuration = uiIntraFrameTimer.Duration(); //持续时间赋值 m_UIUpdateTimer.Start(); //UI更新时间计数 }
/// <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(); }
/// <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(); }