// Determine the current tally sate. If you specify a timeout then it will wait until it has changed, otherwise it will simply poll it // and return the current tally immediately. The return value is whether anything has actually change (true) or whether it timed out (false) public bool GetTally(ref NDIlib.tally_t tally, int timeout) { if (_sendInstancePtr == IntPtr.Zero) { return(false); } return(NDIlib.send_get_tally(_sendInstancePtr, ref tally, (uint)timeout)); }
void SetTallyIndicators(bool onProgram, bool onPreview) { // we need to have a receive instance if (_recvInstancePtr != IntPtr.Zero) { // set up a state descriptor NDIlib.tally_t tallyState = new NDIlib.tally_t() { on_program = onProgram, on_preview = onPreview }; // set it on the receiver instance NDIlib.recv_set_tally(_recvInstancePtr, ref tallyState); } }
// private void SendThreadProc() { // look for changes in tally bool lastProg = false; bool lastPrev = false; NDIlib.tally_t tally = new NDIlib.tally_t(); tally.on_program = lastProg; tally.on_preview = lastPrev; while (!exitThread) { if (Monitor.TryEnter(sendInstanceLock)) { // if this is not here, then we must be being reconfigured if (sendInstancePtr == null) { // unlock Monitor.Exit(sendInstanceLock); // give up some time Thread.Sleep(20); // loop again continue; } try { // get the next available frame NDIlib.video_frame_v2_t frame; if (pendingFrames.TryTake(out frame, 250)) { // this dropps frames if the UI is rendernig ahead of the specified NDI frame rate while (pendingFrames.Count > 1) { NDIlib.video_frame_v2_t discardFrame = pendingFrames.Take(); Marshal.FreeHGlobal(discardFrame.p_data); } // We now submit the frame. Note that this call will be clocked so that we end up submitting // at exactly the requested rate. // If WPF can't keep up with what you requested of NDI, then it will be sent at the rate WPF is rendering. //if (!isPausedValue) if (FInSend[0]) { NDIlib.send_send_video_v2(sendInstancePtr, ref frame); } // free the memory from this frame Marshal.FreeHGlobal(frame.p_data); } } catch (OperationCanceledException) { pendingFrames.CompleteAdding(); } catch { // } // unlock Monitor.Exit(sendInstanceLock); } else { Thread.Sleep(20); } // check tally NDIlib.send_get_tally(sendInstancePtr, ref tally, 0); // if tally changed trigger an update if (lastProg != tally.on_program || lastPrev != tally.on_preview) { // save the last values lastProg = tally.on_program; lastPrev = tally.on_preview; } } }
public void PrepareFrame(string SenderToUse, long timeCode, int shift = 0, int markHeight = 250) { { int exists = senderNameList.IndexOf(SenderToUse); if (exists != -1) { Sender sendInstance = senderList[exists]; // because we are clocking to the video it is better to always submit the audio first // put tone in it every 25 frames double frameNumber = senderFrameSentList[exists]; bool dotone = frameNumber % 25 == 0; // FillAudioBuffer(audioFrame, dotone); // submit the audio buffer doSend = CoreSettings.CoreSettings.Default.SendNDI; // if (doSend) sendInstance.Send(audioFrame); // get the tally state of this source (we poll it), NDIlib.tally_t ndi_tally = sendInstance.Tally; if ((CoreSettings.CoreSettings.Default.MarkFrames) || (CoreSettings.CoreSettings.Default.CheckDroppedFrames)) { Bitmap bmp = new Bitmap(senderVideoFrameList[2 * exists + shift].Width, senderVideoFrameList[2 * exists + shift].Height, senderVideoFrameList[2 * exists + shift].Stride, System.Drawing.Imaging.PixelFormat.Format32bppPArgb, senderVideoFrameList[2 * exists + shift].BufferPtr); Graphics graphics = Graphics.FromImage(bmp); graphics.SmoothingMode = SmoothingMode.AntiAlias; Pen outlinePen = new Pen(Color.Black, 2.0f); Pen thinOutlinePen = new Pen(Color.Black, 1.0f); Pen lineOutlinePen = new Pen(Color.White, 10.0f); Pen transparentlineOutlinePen = new Pen(Color.Transparent, 100.0f); switch (exists) { case 0: lineOutlinePen = new Pen(Color.Red, 10.0f); break; case 1: lineOutlinePen = new Pen(Color.Blue, 10.0f); break; case 2: lineOutlinePen = new Pen(Color.Green, 10.0f); break; case 3: lineOutlinePen = new Pen(Color.Yellow, 10.0f); break; } ; if (CoreSettings.CoreSettings.Default.MarkFrames) { DrawPrettyText(graphics, String.Format("Frame {0}", frameNumber.ToString()), 96.0f, fontFamily, new Point(videoFrame.Width / 2, exists * 80 + 50), textFormat, Brushes.White, outlinePen); } if (CoreSettings.CoreSettings.Default.MarkFrames) { DrawPrettyText(graphics, System.DateTime.Now.ToString(), 96.0f, fontFamily, new Point(videoFrame.Width / 2, 1000), textFormat, Brushes.White, outlinePen); } if (CoreSettings.CoreSettings.Default.CheckDroppedFrames) { int lineHeight = videoFrame.Height / senderFrameSentList.Count; int a = (int)(senderFrameSentList[exists] * 20) % videoFrame.Width; Point pt1 = new Point(a, (videoFrame.Height - (lineHeight * (exists) + markHeight))); Point pt2 = new Point(a, videoFrame.Height - lineHeight * (exists)); Point pt3 = new Point(a, 0); Point pt4 = new Point(a, videoFrame.Height); graphics.CompositingMode = CompositingMode.SourceCopy; graphics.DrawLine(transparentlineOutlinePen, pt3, pt4); graphics.DrawLine(lineOutlinePen, pt1, pt2); graphics.CompositingMode = CompositingMode.SourceOver; } graphics.Dispose(); bmp.Dispose(); } // we now submit the frame. note that this call will be clocked so that we end up submitting // at exactly 25fps. // if (doSend) sendInstance.SendAsync(senderVideoFrameList[2 * exists + shift]); senderVideoFrameList[2 * exists + shift].TimeCode = timeCode; senderFrameSentList[exists]++; } } // using bmp and graphics } // using audioFrame and videoFrame
// the receive thread runs though this loop until told to exit private void SendThreadProc() { // look for changes in tally bool lastProg = false; bool lastPrev = false; NDIlib.tally_t tally = new NDIlib.tally_t { on_program = lastProg, on_preview = lastPrev }; while (!exitThread) { if (Monitor.TryEnter(sendInstanceLock)) { // if this is not here, then we must be being reconfigured if (sendInstancePtr == null) { // unlock Monitor.Exit(sendInstanceLock); // give up some time Thread.Sleep(20); // loop again continue; } // Audio should be send first if (audioEnabled) { try { // get the next available frame if (pendingAudioFrames.TryTake(out NDIlib.audio_frame_v2_t frame, 250)) { // Submit the audio buffer if (!IsSendPaused) { NDIlib.send_send_audio_v2(sendInstancePtr, ref frame); } // free the memory from this frame Marshal.FreeHGlobal(frame.p_data); } } catch (OperationCanceledException) { pendingAudioFrames.CompleteAdding(); } catch {} } try { // get the next available frame if (pendingVideoFrames.TryTake(out NDIlib.video_frame_v2_t frame, 250)) { // this drops frames if the UI is rendering ahead of the specified NDI frame rate while (pendingVideoFrames.Count > 1) { NDIlib.video_frame_v2_t discardFrame = pendingVideoFrames.Take(); Marshal.FreeHGlobal(discardFrame.p_data); } // We now submit the frame. Note that this call will be clocked so that we end up submitting // at exactly the requested rate. // If WPF can't keep up with what you requested of NDI, then it will be sent at the rate WPF is rendering. if (!IsSendPaused) { NDIlib.send_send_video_v2(sendInstancePtr, ref frame); } // free the memory from this frame Marshal.FreeHGlobal(frame.p_data); } } catch (OperationCanceledException) { pendingVideoFrames.CompleteAdding(); } catch {} // unlock Monitor.Exit(sendInstanceLock); } else { Thread.Sleep(20); } // check tally NDIlib.send_get_tally(sendInstancePtr, ref tally, 0); // if tally changed trigger an update if (lastProg != tally.on_program || lastPrev != tally.on_preview) { // save the last values lastProg = tally.on_program; lastPrev = tally.on_preview; // set these on the UI thread Application.Current.Dispatcher.BeginInvoke(new Action(() => { IsOnProgram = lastProg; IsOnPreview = lastPrev; })); } } }
public void Evaluate(int SpreadMax) { if (FInUpdate[0]) { string msg = ""; frameNumber++; // fill it with a lovely color graphics.Clear(Color.Maroon); // show which source we are msg = DrawPrettyText(graphics, "C# Example Source", 96.0f, fontFamily, new Point(960, 100), textFormat, Brushes.White, outlinePen); if (msg != "") { FLogger.Log(LogType.Error, "DrawPrettyText error: " + msg); } try { // Get the tally state of this source (we poll it), NDIlib.tally_t NDI_tally = new NDIlib.tally_t(); NDIlib.send_get_tally(sendInstancePtr, ref NDI_tally, 0); // Do something different depending on where we are shown if (NDI_tally.on_program) { msg = DrawPrettyText(graphics, "On Program", 96.0f, fontFamily, new Point(960, 225), textFormat, Brushes.White, outlinePen); if (msg != "") { FLogger.Log(LogType.Error, "DrawPrettyText error: " + msg); } } else if (NDI_tally.on_preview) { msg = DrawPrettyText(graphics, "On Preview", 96.0f, fontFamily, new Point(960, 225), textFormat, Brushes.White, outlinePen); if (msg != "") { FLogger.Log(LogType.Error, "DrawPrettyText error: " + msg); } } } catch (Exception e) { FLogger.Log(LogType.Error, "Update: " + e.Message); } //// show what frame we've rendered msg = DrawPrettyText(graphics, String.Format("Frame {0}", frameNumber.ToString()), 96.0f, fontFamily, new Point(960, 350), textFormat, Brushes.White, outlinePen); if (msg != "") { FLogger.Log(LogType.Error, "DrawPrettyText error: " + msg); } // show current time msg = DrawPrettyText(graphics, System.DateTime.Now.ToString(), 96.0f, fontFamily, new Point(960, 900), textFormat, Brushes.White, outlinePen); if (msg != "") { FLogger.Log(LogType.Error, "DrawPrettyText error: " + msg); } } if (FInSend[0]) { // are we connected to anyone? //if (NDI.Send.NDIlib_send_get_no_connections(sendInstancePtr, 10000) < 1) if (NDIlib.send_get_no_connections(sendInstancePtr, FInTimeout[0]) < 1) { // no point rendering FLogger.Log(LogType.Debug, "No current connections, so no rendering needed."); //Console.WriteLine("No current connections, so no rendering needed."); // Wait a bit, otherwise our limited example will end before you can connect to it System.Threading.Thread.Sleep(50); } else { try { // We now submit the frame. Note that this call will be clocked so that we end up submitting // at exactly 29.97fps. NDIlib.send_send_video_v2(sendInstancePtr, ref videoFrame); // Just display something helpful in the console FLogger.Log(LogType.Debug, "Frame number " + frameNumber + " sent."); } catch (Exception e) { FLogger.Log(LogType.Error, "Send: " + e.Message); } } } }