using skyscraper5.Mpeg2; using skyscraper5.Skyscraper.Scraper; using skyscraper5.Skyscraper.Scraper.Storage; using skyscraper5.UI.Overrides; using skyscraper5.UI.StreamAcquisition; namespace skyscraper5.UI { public partial class Form1 : Form, INodeEngine { public Form1() { InitializeComponent(); } public Form1(StreamSource acquiredStream) : this() { AcquiredStream = acquiredStream; tsContext = new TsContext(); tsContext.OnPacketLoss += TsContext_OnPacketLoss; skyscraperContext = new SkyscraperContext(tsContext, eventLogger, storage); skyscraperContext.UiJunction = uiJunction; } private void TsContext_OnPacketLoss(uint pid, uint oldContinuity, uint newContinuity, long packetLossEvents) { continuityErrors = packetLossEvents; } public StreamSource AcquiredStream { get; } private TsContext tsContext; private SkyscraperContext skyscraperContext; private ISkyscraperEventLogger eventLogger; private IScraperStroage storage; private Form1UiJunction uiJunction; private DateTime windowOpenedTimestamp; private TimeSpan windowOpenDuration; private long continuityErrors; private TeiCounter teiCounterFilter; private PmtCounter pmtCounter; private PacketsPerSecondCounter packetsPerSecondCounter; private ScrambleCounter scrambleCounter; private void Form1_Load(object sender, EventArgs e) { scrambleCounter = new ScrambleCounter(); packetsPerSecondCounter = new PacketsPerSecondCounter(); teiCounterFilter = new TeiCounter(); skyscraperContext.InitalizeFilterChain(teiCounterFilter, packetsPerSecondCounter, scrambleCounter); uiJunction = new Form1UiJunction(this); skyscraperContext.UiJunction = uiJunction; windowOpenedTimestamp = DateTime.Now; windowOpenDuration = new TimeSpan(0, 0, 0); bottomLeft.Items[0].SubItems[1].Text = AcquiredStream.GetSourceName(); skyscraperContext.PatDecoder.OnValidSection += PsiDecoder_OnValidSection; skyscraperContext.PatDecoder.OnCrcError += PsiDecoder_OnCrcError; pmtCounter = new PmtCounter(); skyscraperContext.PmtDecoderTransformer = pmtCounter; skyscraperContext.CatDecoder.OnValidSection += PsiDecoder_OnValidSection; skyscraperContext.CatDecoder.OnCrcError += PsiDecoder_OnCrcError; skyscraperContext.NitDecoder.OnValidSection += PsiDecoder_OnValidSection; skyscraperContext.NitDecoder.OnCrcError += PsiDecoder_OnCrcError; skyscraperContext.Pid0x11Decoder.OnValidSection += PsiDecoder_OnValidSection; skyscraperContext.Pid0x11Decoder.OnCrcError += PsiDecoder_OnCrcError; skyscraperContext.Pid0x12Decoder.OnValidSection += PsiDecoder_OnValidSection; skyscraperContext.Pid0x12Decoder.OnCrcError += PsiDecoder_OnCrcError; DrawBottomCenter(); pidListView_ColumnClick(this, new ColumnClickEventArgs(0)); AcquiredStream.StartSource(skyscraperContext); } private long patErrors, catErrors, nitErrors, sdtErrors, batErrors, eitErrors; private void PsiDecoder_OnCrcError(PsiSection section, int pid, long available) { switch (section.TableId) { case 0: patErrors++; break; case 1: catErrors++; break; case 0x40: nitErrors++; break; case 0x41: nitErrors++; break; case 0x42: sdtErrors++; break; case 0x46: sdtErrors++; break; case 0x4a: batErrors++; break; default: if (section.TableId >= 0x4e && section.TableId <= 0x6f) { eitErrors++; break; } throw new NotImplementedException(); } } private long patValids, catValids, nitValids, sdtValids, batValids, eitValids; private void PsiDecoder_OnValidSection(PsiSection section, int pid, long available) { switch (section.TableId) { case 0: patValids++; break; case 1: catValids++; break; case 0x40: nitValids++; break; case 0x41: nitValids++; break; case 0x42: sdtValids++; break; case 0x46: sdtValids++; break; case 0x4a: batValids++; break; default: if (section.TableId >= 0x4e && section.TableId <= 0x6f) { eitValids++; break; } throw new NotImplementedException(); } } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { AcquiredStream.StopSource(); } private void timer1_Tick(object sender, EventArgs e) { windowOpenDuration = windowOpenDuration.Add(new TimeSpan(0, 0, 1)); bottomLeft.Items[1].SubItems[1].Text = windowOpenDuration.ToString(); DrawBottomCenter(); RenderPidList(); bottomLeft.Items[2].SubItems[1].Text = AcquiredStream.State.ToString(); } private string bottomCenterTemplate; private void DrawBottomCenter() { if (string.IsNullOrWhiteSpace(bottomCenterTemplate)) { bottomCenterTemplate = bottomCenter.Text; } long patSections = patValids; long pmtSections = pmtCounter.ValidSection; ; long catSections = catValids; long nitSections = nitValids; long sdtSections = sdtValids; long eitSections = eitValids; long patCrcErrors = patErrors; long pmtCrcErrors = pmtCounter.CrcErrors; long catCrcErrors = catErrors; long nitCrcErrors = nitErrors; long sdtCrcErrors = sdtErrors; long eitCrcErrors = eitErrors; long continuityErrors = this.continuityErrors; string muxBitrate = "???"; long teiErrors = this.teiCounterFilter.TransportErrors; string lastSec = RenderPacketsPerSecond(); long syncLosses = 0; long inBuffer = 0; long outBuffer = 0; bottomCenter.Text = String.Format(bottomCenterTemplate, patSections, pmtSections, catSections, nitSections, sdtSections, eitSections, patCrcErrors, pmtCrcErrors, catCrcErrors, nitCrcErrors, sdtCrcErrors, eitCrcErrors, continuityErrors, muxBitrate, teiErrors, lastSec, syncLosses, inBuffer, outBuffer); } private char[] dataUnits = { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' }; private string RenderPacketsPerSecond() { if (!packetsPerSecondCounter.Ready) { return "???"; } long packetsPerSecond = packetsPerSecondCounter.PacketsPerSecond; long bytesPerSecond = packetsPerSecond * 188; long bitsPerSecond = bytesPerSecond * 8; if (bitsPerSecond < 1000) { return String.Format("{0} bits/s", bytesPerSecond); } int unitOffset = -1; while (bitsPerSecond > 1000) { bitsPerSecond /= 1000; unitOffset++; } return String.Format("{0} {1}bits/s", bitsPerSecond, dataUnits[unitOffset]); } private PidListViewItem[] listViewItems; private Color scrambledColor = Color.FromArgb(255, 255, 128, 128); private Color unscrambledColor = Color.FromArgb(255, 128, 255, 128); private void RenderPidList() { if (listViewItems == null) listViewItems = new PidListViewItem[0x2000]; ulong[] ulongs = tsContext.GetPidStatistics(); for (int i = 0; i < 0x2000; i++) { if (ulongs[i] != 0) { if (listViewItems[i] == null) { listViewItems[i] = new PidListViewItem(i); pidListView.Items.Add(listViewItems[i]); } listViewItems[i].SetPackageStats(ulongs[i], packetsPerSecondCounter.TotalPacketsOverall); listViewItems[i].BackColor = scrambleCounter.IsScrambled(i) ? scrambledColor : unscrambledColor; } if (uiJunction.PidToPrograms != null && uiJunction.SdtNames != null) { if (uiJunction.PidToPrograms.ContainsKey(i)) { uint program = uiJunction.PidToPrograms[i]; if (uiJunction.SdtNames.ContainsKey(program)) { listViewItems[i].ProgramName = uiJunction.SdtNames[program]; } } } if (uiJunction.UsageLabels != null) { if (uiJunction.UsageLabels.ContainsKey(i)) { if (listViewItems[i] != null) { listViewItems[i].UsageLabel = uiJunction.UsageLabels[i]; } } } } } private PidListViewItemSorter lvwColumnSorter; private void pidListView_ColumnClick(object sender, ColumnClickEventArgs e) { if (lvwColumnSorter == null) { lvwColumnSorter = new PidListViewItemSorter(); pidListView.ListViewItemSorter = lvwColumnSorter; } if (e.Column == lvwColumnSorter.SortColumn) { if (lvwColumnSorter.Order == SortOrder.Ascending) { lvwColumnSorter.Order = SortOrder.Descending; } else { lvwColumnSorter.Order = SortOrder.Ascending; } } else { // Set the column number that is to be sorted; default to ascending. lvwColumnSorter.SortColumn = e.Column; lvwColumnSorter.Order = SortOrder.Ascending; } pidListView.Sort(); } SkyscraperUiNode INodeEngine.EnsureNodeExists(params string[] path) { SkyscraperUiNode result = null; if (IsDisposed) return null; Invoke(new Action(() => { result = EnsureNodeExistsEx(path); })); return result; } private SkyscraperUiNode EnsureNodeExistsEx(params string[] path) { TreeNodeCollection rootNodes = treeView1.Nodes; SkyscraperUiNode parent = null; for (int i = 0; i < path.Length; i++) { SkyscraperUiNode foundNode = null; if (i == 0) { foreach (TreeNode rootNode in rootNodes) { if (rootNode.Text.Equals(path[i])) { foundNode = (SkyscraperUiNode)rootNode; break; } } if (foundNode == null) { foundNode = new SkyscraperUiNode(); foundNode.Text = path[i]; rootNodes.Add(foundNode); } } else { foreach (TreeNode childNode in parent.Nodes) { if (childNode.Text.Equals(path[i])) { foundNode = (SkyscraperUiNode)childNode; break; } } if (foundNode == null) { foundNode = new SkyscraperUiNode(); foundNode.Text = path[i]; parent.Nodes.Add(foundNode); } } parent = foundNode; } return parent; } public void RunOnUiThread(Action action) { Invoke(action); } private void treeView1_AfterSelect_1(object sender, TreeViewEventArgs e) { SkyscraperUiNode node = e.Node as SkyscraperUiNode; if (node == null) return; textBox1.Text = node.DetailedText; } public bool TestForNode(params string[] path) { TreeNodeCollection rootNodes = treeView1.Nodes; SkyscraperUiNode parent = null; for (int i = 0; i < path.Length; i++) { SkyscraperUiNode foundNode = null; if (i == 0) { foreach (TreeNode rootNode in rootNodes) { if (rootNode.Text.Equals(path[i])) { foundNode = (SkyscraperUiNode)rootNode; break; } } if (foundNode == null) { return false; } } else { foreach (TreeNode childNode in parent.Nodes) { if (childNode.Text.Equals(path[i])) { foundNode = (SkyscraperUiNode)childNode; break; } } if (foundNode == null) { return false; } } parent = foundNode; } return true; } void INodeEngine.SetIcon(SkyscraperUiNode node, int index) { RunOnUiThread(() => { node.ImageIndex = index; node.SelectedImageIndex = index; }); } } }