├── SimpleMJPEGStreamViewer ├── Resources │ ├── cam.png │ ├── del.png │ ├── eye.png │ ├── minus.png │ ├── plus.png │ ├── gradient.png │ ├── notready.png │ ├── ic_lens_grey_24dp.png │ ├── ic_delete_grey600_24dp.png │ ├── ic_delete_grey600_24dp1.png │ ├── ic_perm_media_grey600_24dp.png │ ├── ic_visibility_grey600_24dp.png │ ├── ic_chevron_left_grey600_24dp.png │ ├── ic_chevron_left_grey600_36dp.png │ ├── ic_chevron_right_grey600_24dp.png │ ├── ic_my_library_add_black_24dp.png │ ├── ic_my_library_add_grey600_24dp.png │ ├── ic_my_library_add_grey600_36dp.png │ ├── ic_visibility_off_grey600_24dp.png │ ├── ic_my_library_add_grey600_24dp1.png │ ├── ic_play_circle_outline_grey600_24dp.png │ ├── ic_settings_overscan_grey600_24dp.png │ └── ic_pause_circle_outline_grey600_24dp.png ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── packages.config ├── SimpleBindingList.cs ├── Program.cs ├── SimpleDataGridView.cs ├── SimplePictureBox.cs ├── VideoItem.cs ├── app.manifest ├── default.xml ├── SimpleLayoutPanel.cs ├── SimpleMJPEGDecoder.cs ├── SimpleMJPEGDecoderDataFlow.cs ├── MainForm.resx ├── SimpleMJPEGStreamViewer.csproj ├── MainForm.cs └── MainForm.Designer.cs ├── README.md ├── SimpleMJPEGStreamViewer.sln ├── LICENSE ├── .gitattributes └── .gitignore /SimpleMJPEGStreamViewer/Resources/cam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/cam.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/del.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/eye.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/minus.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/plus.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/gradient.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/notready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/notready.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_lens_grey_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_lens_grey_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_delete_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_delete_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_delete_grey600_24dp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_delete_grey600_24dp1.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_perm_media_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_perm_media_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_visibility_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_visibility_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_chevron_left_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_chevron_left_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_chevron_left_grey600_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_chevron_left_grey600_36dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_chevron_right_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_chevron_right_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_my_library_add_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_my_library_add_black_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_my_library_add_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_my_library_add_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_my_library_add_grey600_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_my_library_add_grey600_36dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_visibility_off_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_visibility_off_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_my_library_add_grey600_24dp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_my_library_add_grey600_24dp1.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_play_circle_outline_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_play_circle_outline_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_settings_overscan_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_settings_overscan_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Resources/ic_pause_circle_outline_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Larry57/SimpleMJPEGStreamViewer/HEAD/SimpleMJPEGStreamViewer/Resources/ic_pause_circle_outline_grey600_24dp.png -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimpleBindingList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace SimpleMJPEGStreamViewer { 5 | public class SimpleBindingList : BindingList { 6 | public event Action BeforeRemove; 7 | protected override void RemoveItem(int itemIndex) { 8 | if(BeforeRemove != null) { 9 | BeforeRemove(this, this.Items[itemIndex]); 10 | } 11 | base.RemoveItem(itemIndex); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace SimpleMJPEGStreamViewer { 5 | static class Program { 6 | /// 7 | /// Point d'entrée principal de l'application. 8 | /// 9 | [STAThread] 10 | static void Main() { 11 | Application.EnableVisualStyles(); 12 | Application.SetCompatibleTextRenderingDefault(false); 13 | Application.Run(new MainForm()); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #SimpleMJPEGStreamViewer 2 | 3 | This simple MJPEG streams viewer is a proof to myself that MJPEG stream decoding can be accomplished using bare C# bricks and a naive, **very easy to adapt** implementation. 4 | 5 | ![enter image description here](https://i.imgsafe.org/22e2964.png) 6 | 7 | The core is the *SimpleMJPEGDecoder.cs* class which is available as is, separately [in a Gist.](https://gist.github.com/Larry57/958bd61252a3f810b75d) 8 | 9 | **I am still looking for a way to decode the more MJPEG streams I could at the same time**, and posted [a question on CodeReview](http://codereview.stackexchange.com/questions/123394/bare-bone-c-mjpeg-stream-decoder-implementation) regarding this. 10 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimpleDataGridView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | 8 | namespace SimpleMJPEGStreamViewer { 9 | class SimpleDataGridView : DataGridView { 10 | 11 | public Action SaveSelection; 12 | public Action RestoreSelection; 13 | 14 | protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e) { 15 | if(SaveSelection != null) 16 | SaveSelection(e.ColumnIndex); 17 | 18 | base.OnCellMouseDown(e); 19 | 20 | if(RestoreSelection != null) 21 | RestoreSelection(); 22 | } 23 | 24 | protected override void OnCellDoubleClick(DataGridViewCellEventArgs e) { 25 | if(SaveSelection != null) 26 | SaveSelection(e.ColumnIndex); 27 | 28 | base.OnCellDoubleClick(e); 29 | 30 | if(RestoreSelection != null) 31 | RestoreSelection(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleMJPEGStreamViewer", "SimpleMJPEGStreamViewer\SimpleMJPEGStreamViewer.csproj", "{FE05CD86-48CA-47AD-9CAD-5F5E93EDC377}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {FE05CD86-48CA-47AD-9CAD-5F5E93EDC377}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {FE05CD86-48CA-47AD-9CAD-5F5E93EDC377}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {FE05CD86-48CA-47AD-9CAD-5F5E93EDC377}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {FE05CD86-48CA-47AD-9CAD-5F5E93EDC377}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 L4rry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SimpleMJPEGStreamViewer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimplePictureBox.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Forms; 3 | 4 | namespace SimpleMJPEGStreamViewer { 5 | public class SimplePictureBox : Control { 6 | 7 | public SimplePictureBox() { 8 | DoubleBuffered = true; 9 | var timer = new System.Timers.Timer(1000); 10 | timer.Elapsed += (e, s) => { 11 | fps = frameCount.ToString(); 12 | frameCount = 0; 13 | Invalidate(); 14 | }; 15 | timer.Start(); 16 | } 17 | 18 | string fps; 19 | int frameCount; 20 | 21 | Image image; 22 | 23 | public Image Image { 24 | get { 25 | return image; 26 | } 27 | set { 28 | if(image != null) 29 | image.Dispose(); 30 | 31 | image = value; 32 | 33 | frameCount++; 34 | Invalidate(); 35 | } 36 | } 37 | 38 | protected override void OnPaint(PaintEventArgs e) { 39 | base.OnPaint(e); 40 | if(Image != null) 41 | e.Graphics.DrawImage(Image, this.ClientRectangle); 42 | e.Graphics.DrawString(fps, this.Font, Brushes.Red, 20, 20); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // Les informations générales relatives à un assembly dépendent de 5 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 6 | // associées à un assembly. 7 | [assembly: AssemblyTitle("SimpleMJPEGStreamViewer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("SimpleMJPEGStreamViewer")] 12 | [assembly: AssemblyCopyright("Copyright © 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 17 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 18 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 19 | [assembly: ComVisible(false)] 20 | 21 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 22 | [assembly: Guid("fe05cd86-48ca-47ad-9cad-5f5e93edc377")] 23 | 24 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 25 | // 26 | // Version principale 27 | // Version secondaire 28 | // Numéro de build 29 | // Révision 30 | // 31 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 32 | // en utilisant '*', comme indiqué ci-dessous : 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/VideoItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Threading; 4 | using System.Xml.Serialization; 5 | 6 | namespace SimpleMJPEGStreamViewer { 7 | public class VideoItem : INotifyPropertyChanged { 8 | 9 | public event PropertyChangedEventHandler PropertyChanged; 10 | 11 | public VideoItem() { 12 | UUID = Guid.NewGuid(); 13 | Playing = false; 14 | Name = "Unnamed"; 15 | MaxStreamBufferSize = 1024; 16 | } 17 | 18 | string login; 19 | public string Login { 20 | get { 21 | return login; 22 | } 23 | set { 24 | if(login == value) 25 | return; 26 | 27 | login = value; 28 | 29 | OnPropertyChanged(new PropertyChangedEventArgs(nameof(Login))); 30 | } 31 | } 32 | 33 | string password; 34 | public string Password { 35 | get { 36 | return password; 37 | } 38 | set { 39 | if(password == value) 40 | return; 41 | 42 | password = value; 43 | 44 | OnPropertyChanged(new PropertyChangedEventArgs(nameof(Password))); 45 | } 46 | } 47 | 48 | string name; 49 | public string Name { 50 | get { 51 | return name; 52 | } 53 | set { 54 | if(name == value) 55 | return; 56 | 57 | name = value; 58 | 59 | OnPropertyChanged(new PropertyChangedEventArgs(nameof(Name))); 60 | } 61 | } 62 | 63 | string url; 64 | public string Url { 65 | get { 66 | return url; 67 | } 68 | set { 69 | if(url == value) 70 | return; 71 | 72 | url = value; 73 | 74 | OnPropertyChanged(new PropertyChangedEventArgs(nameof(Url))); 75 | } 76 | } 77 | 78 | bool status; 79 | public bool Playing { 80 | get { 81 | return status; 82 | } 83 | set { 84 | if(status == value) 85 | return; 86 | 87 | status = value; 88 | 89 | if(!value && Cts != null) 90 | Cts.Cancel(); 91 | 92 | OnPropertyChanged(new PropertyChangedEventArgs(nameof(Playing))); 93 | } 94 | } 95 | 96 | [XmlIgnore] 97 | public CancellationTokenSource Cts { 98 | set; private get; 99 | } 100 | 101 | public readonly Guid UUID; 102 | 103 | bool visible; 104 | public bool Visible { 105 | get { 106 | return visible; 107 | } 108 | set { 109 | if(visible == value) 110 | return; 111 | 112 | visible = value; 113 | 114 | OnPropertyChanged(new PropertyChangedEventArgs(nameof(Visible))); 115 | } 116 | } 117 | 118 | public int MaxStreamBufferSize { get; set; } 119 | 120 | protected void OnPropertyChanged(PropertyChangedEventArgs e) { 121 | if(PropertyChanged != null) { 122 | var handler = PropertyChanged; 123 | if(handler != null) 124 | handler(this, e); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | true 57 | 58 | 59 | 60 | 61 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://83.150.146.150/axis-cgi/mjpg/video.cgi 5 | http://83.150.146.150/axis-cgi/mjpg/video.cgi 6 | true 7 | true 8 | 1024 9 | 10 | 11 | http://lwsnb148-cam.cs.purdue.edu/axis-cgi/mjpg/video.cgi 12 | http://lwsnb148-cam.cs.purdue.edu/axis-cgi/mjpg/video.cgi 13 | true 14 | true 15 | 1024 16 | 17 | 18 | http://142.36.244.87:8888/axis-cgi/mjpg/video.cgi 19 | http://142.36.244.87:8888/axis-cgi/mjpg/video.cgi 20 | true 21 | true 22 | 1024 23 | 24 | 25 | http://206.140.121.226/axis-cgi/mjpg/video.cgi 26 | http://206.140.121.226/axis-cgi/mjpg/video.cgi 27 | true 28 | true 29 | 1024 30 | 31 | 32 | http://72.240.51.210/axis-cgi/mjpg/video.cgi 33 | http://72.240.51.210/axis-cgi/mjpg/video.cgi 34 | true 35 | true 36 | 1024 37 | 38 | 39 | http://80.254.191.189:8008/axis-cgi/mjpg/video.cgi 40 | http://80.254.191.189:8008/axis-cgi/mjpg/video.cgi 41 | true 42 | true 43 | 1024 44 | 45 | 46 | http://194.89.9.86/axis-cgi/mjpg/video.cgi 47 | http://194.89.9.86/axis-cgi/mjpg/video.cgi 48 | true 49 | true 50 | 1024 51 | 52 | 53 | http://oviso.axiscam.net/axis-cgi/mjpg/video.cgi 54 | http://oviso.axiscam.net/axis-cgi/mjpg/video.cgi 55 | true 56 | true 57 | 1024 58 | 59 | 60 | http://webcam1.lpl.org/axis-cgi/mjpg/video.cgi 61 | http://webcam1.lpl.org/axis-cgi/mjpg/video.cgi 62 | true 63 | true 64 | 1024 65 | 66 | 67 | http://200.33.20.122:2007/axis-cgi/mjpg/video.cgi 68 | http://200.33.20.122:2007/axis-cgi/mjpg/video.cgi 69 | true 70 | true 71 | 1024 72 | 73 | 74 | http://66.193.157.18/axis-cgi/mjpg/video.cgi 75 | http://66.193.157.18/axis-cgi/mjpg/video.cgi 76 | true 77 | true 78 | 1024 79 | 80 | 81 | http://82.89.169.171/axis-cgi/mjpg/video.cgi 82 | http://82.89.169.171/axis-cgi/mjpg/video.cgi 83 | true 84 | true 85 | 1024 86 | 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimpleLayoutPanel.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows.Forms; 7 | 8 | namespace SimpleMJPEGStreamViewer { 9 | public partial class SimpleLayoutPanel : Panel { 10 | public SimpleLayoutPanel() { 11 | DoubleBuffered = true; 12 | } 13 | 14 | readonly public int HorizontalSpace = 1; 15 | readonly public int VerticalSpace = 1; 16 | readonly public double Ratio = 4/3d; 17 | 18 | protected override void OnLayout(LayoutEventArgs levent) { 19 | base.OnLayout(levent); 20 | 21 | ReArrangeControls(); 22 | Invalidate(true); 23 | } 24 | 25 | public void ReArrangeControls() { 26 | if(!Visible) 27 | return; 28 | this.SuspendLayout(); 29 | arrange(this.Width, this.Height, HorizontalSpace, VerticalSpace, Ratio, this.Controls.OfType().Where(ctl => ctl.Visible)); 30 | this.ResumeLayout(false); 31 | } 32 | 33 | class ItemSize { 34 | public int Cols; 35 | public int Rows; 36 | public int Width; 37 | public int Height; 38 | } 39 | 40 | static ItemSize calculateBestItemSize(int w, int h, int n, int horizontalGap, int verticalGap, double ratio) { 41 | if(n == 0) 42 | return null; 43 | 44 | return ( 45 | 46 | // Try n rows 47 | Enumerable.Range(1, n).Select(testRowCount => { 48 | var testItemHeight = (h - verticalGap * (testRowCount - 1)) / testRowCount; 49 | 50 | return new ItemSize { 51 | Cols = (int)Math.Ceiling((double)n / testRowCount), 52 | Rows = testRowCount, 53 | Height = (int)testItemHeight, 54 | Width = (int)(testItemHeight * ratio) 55 | }; 56 | }) 57 | 58 | // Try n columns 59 | .Concat( 60 | Enumerable.Range(1, n).Select(testColCount => { 61 | var testItemWidth = (w - horizontalGap * (testColCount - 1)) / testColCount; 62 | 63 | return new ItemSize { 64 | Cols = testColCount, 65 | Rows = (int)Math.Ceiling((double)n / testColCount), 66 | Height = (int)(testItemWidth / ratio), 67 | Width = (int)testItemWidth 68 | }; 69 | }))) 70 | 71 | // Remove when it's too big 72 | .Where(item => item.Width * item.Cols + horizontalGap * (item.Cols - 1) <= w && 73 | item.Height * item.Rows + verticalGap * (item.Rows - 1) <= h) 74 | 75 | // Get the biggest area 76 | .OrderBy(item => item.Height * item.Width) 77 | .LastOrDefault(); 78 | } 79 | 80 | static void arrange(int width, int height, int horizontalGap, int verticalGap, double aspectRatio, IEnumerable controls) { 81 | 82 | var count = controls.Count(); 83 | if(count == 0) 84 | return; 85 | 86 | var bestSizedItem = calculateBestItemSize(width, height, count, horizontalGap, verticalGap, aspectRatio); 87 | if(bestSizedItem == null) 88 | return; 89 | 90 | // Centering 91 | var xCenter = (width - (bestSizedItem.Width * bestSizedItem.Cols + bestSizedItem.Cols * horizontalGap - horizontalGap)) / 2; 92 | var y = (height - (bestSizedItem.Height * bestSizedItem.Rows + bestSizedItem.Rows * verticalGap - verticalGap)) / 2; 93 | 94 | var x = xCenter; 95 | 96 | foreach(var control in controls) { 97 | control.SetBounds(x, y, bestSizedItem.Width, bestSizedItem.Height); 98 | x += bestSizedItem.Width + horizontalGap; 99 | if(x + bestSizedItem.Width - horizontalGap > width) { 100 | x = xCenter; 101 | y += bestSizedItem.Height + verticalGap; 102 | } 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimpleMJPEGDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace SimpleMJPEGStreamViewer { 11 | static class SimpleMJPEGDecoder { 12 | 13 | // JPEG delimiters 14 | const byte picMarker = 0xFF; 15 | const byte picStart = 0xD8; 16 | const byte picEnd = 0xD9; 17 | 18 | /// 19 | /// Start a MJPEG on a http stream 20 | /// 21 | /// Delegate to run at each frame 22 | /// url of the http stream (only basic auth is implemented) 23 | /// optional login 24 | /// optional password (only basic auth is implemented) 25 | /// cancellation token used to cancel the stream parsing 26 | /// Max chunk byte size when reading stream 27 | /// Maximum frame byte size 28 | /// 29 | /// 30 | public async static Task StartAsync(Action action, string url, string login = null, string password = null, CancellationToken? token = null, int chunkMaxSize = 1024, int frameBufferSize = 1024 * 1024) { 31 | 32 | var tok = token ?? CancellationToken.None; 33 | 34 | using(var cli = new HttpClient()) { 35 | if(!string.IsNullOrEmpty(login) && !string.IsNullOrEmpty(password)) 36 | cli.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{login}:{password}"))); 37 | 38 | using(var stream = await cli.GetStreamAsync(url).ConfigureAwait(false)) { 39 | 40 | var streamBuffer = new byte[chunkMaxSize]; // Stream chunk read 41 | var frameBuffer = new byte[frameBufferSize]; // Frame buffer 42 | 43 | var frameIdx = 0; // Last written byte location in the frame buffer 44 | var inPicture = false; // Are we currently parsing a picture ? 45 | byte current = 0x00; // The last byte read 46 | byte previous = 0x00; // The byte before 47 | 48 | // Continuously pump the stream. The cancellationtoken is used to get out of there 49 | while(true) { 50 | var streamLength = await stream.ReadAsync(streamBuffer, 0, chunkMaxSize, tok).ConfigureAwait(false); 51 | parseStreamBuffer(action, frameBuffer, ref frameIdx, streamLength, streamBuffer, ref inPicture, ref previous, ref current); 52 | }; 53 | } 54 | } 55 | } 56 | 57 | // Parse the stream buffer 58 | 59 | static void parseStreamBuffer(Action action, byte[] frameBuffer, ref int frameIdx, int streamLength, byte[] streamBuffer, ref bool inPicture, ref byte previous, ref byte current) { 60 | var idx = 0; 61 | while(idx < streamLength) { 62 | if(inPicture) { 63 | parsePicture(action, frameBuffer, ref frameIdx, ref streamLength, streamBuffer, ref idx, ref inPicture, ref previous, ref current); 64 | } 65 | else { 66 | searchPicture(frameBuffer, ref frameIdx, ref streamLength, streamBuffer, ref idx, ref inPicture, ref previous, ref current); 67 | } 68 | } 69 | } 70 | 71 | // While we are looking for a picture, look for a FFD8 (end of JPEG) sequence. 72 | 73 | static void searchPicture(byte[] frameBuffer, ref int frameIdx, ref int streamLength, byte[] streamBuffer, ref int idx, ref bool inPicture, ref byte previous, ref byte current) { 74 | do { 75 | previous = current; 76 | current = streamBuffer[idx++]; 77 | 78 | // JPEG picture start ? 79 | if(previous == picMarker && current == picStart) { 80 | frameIdx = 2; 81 | frameBuffer[0] = picMarker; 82 | frameBuffer[1] = picStart; 83 | inPicture = true; 84 | return; 85 | } 86 | } while(idx < streamLength); 87 | } 88 | 89 | // While we are parsing a picture, fill the frame buffer until a FFD9 is reach. 90 | 91 | static void parsePicture(Action action, byte[] frameBuffer, ref int frameIdx, ref int streamLength, byte[] streamBuffer, ref int idx, ref bool inPicture, ref byte previous, ref byte current) { 92 | do { 93 | previous = current; 94 | current = streamBuffer[idx++]; 95 | frameBuffer[frameIdx++] = current; 96 | 97 | // JPEG picture end ? 98 | if(previous == picMarker && current == picEnd) { 99 | Image img = null; 100 | 101 | // Using a memorystream this way prevent arrays copy and allocations 102 | using(var s = new MemoryStream(frameBuffer, 0, frameIdx)) { 103 | try { 104 | img = Image.FromStream(s); 105 | } 106 | catch { 107 | // We dont care about badly decoded pictures 108 | } 109 | } 110 | 111 | // Defer the image processing to prevent slow down 112 | // The image processing delegate must dispose the image eventually. 113 | Task.Run(() => action(img)); 114 | inPicture = false; 115 | return; 116 | } 117 | } while(idx < streamLength); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimpleMJPEGDecoderDataFlow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.ServiceModel.Channels; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Threading.Tasks.Dataflow; 11 | 12 | namespace SimpleMJPEGStreamViewer { 13 | class SimpleMJPEGDecoderDataFlow { 14 | 15 | // JPEG delimiters 16 | const byte picMarker = 0xFF; 17 | const byte picStart = 0xD8; 18 | const byte picEnd = 0xD9; 19 | 20 | int frameIdx = 0; // Last written byte location in the frame buffer 21 | bool inPicture = false; // Are we currently parsing a picture ? 22 | byte current = 0x00; // The last byte read 23 | byte previous = 0x00; // The byte before 24 | int idx = 0; 25 | 26 | Action action; 27 | 28 | byte[] frameBuffer; // Frame buffer 29 | 30 | static BufferManager bm = BufferManager.CreateBufferManager(1024 * 2048, 2048); 31 | 32 | static ActionBlock> block = new ActionBlock>( 33 | t => { 34 | t.Item1.parseStreamBuffer(t.Item2, t.Item3); 35 | }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1, MaxMessagesPerTask = 1}); 36 | 37 | /// 38 | /// Start a MJPEG on a http stream 39 | /// 40 | /// Delegate to run at each frame 41 | /// url of the http stream (only basic auth is implemented) 42 | /// optional login 43 | /// optional password (only basic auth is implemented) 44 | /// cancellation token used to cancel the stream parsing 45 | /// Max chunk byte size when reading stream 46 | /// Maximum frame byte size 47 | /// 48 | /// 49 | public async static Task StartAsync(Action action, string url, string login = null, string password = null, CancellationToken? token = null, int chunkMaxSize = 1024, int frameBufferSize = 1024 * 1024) { 50 | 51 | var instance = new SimpleMJPEGDecoderDataFlow { 52 | frameBuffer = new byte[frameBufferSize], 53 | action = action 54 | }; 55 | 56 | var tok = token ?? CancellationToken.None; 57 | 58 | using(var cli = new HttpClient()) { 59 | if(!string.IsNullOrEmpty(login) && !string.IsNullOrEmpty(password)) 60 | cli.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{login}:{password}"))); 61 | 62 | using(var stream = await cli.GetStreamAsync(url).ConfigureAwait(false)) { 63 | 64 | // Continuously pump the stream. The cancellationtoken is used to get out of there 65 | while(true) { 66 | var streamBuffer = bm.TakeBuffer(chunkMaxSize); 67 | var streamLength = await stream.ReadAsync(streamBuffer, 0, chunkMaxSize, tok).ConfigureAwait(false); 68 | await block.SendAsync(Tuple.Create(instance, streamLength, streamBuffer)); 69 | }; 70 | } 71 | } 72 | } 73 | 74 | // Parse the stream buffer 75 | 76 | void parseStreamBuffer(int streamLength, byte[] streamBuffer) { 77 | idx = 0; 78 | while(idx < streamLength) { 79 | if(inPicture) { 80 | parsePicture(streamLength, streamBuffer); 81 | } 82 | else { 83 | searchPicture(streamLength, streamBuffer); 84 | } 85 | } 86 | } 87 | 88 | // While we are looking for a picture, look for a FFD8 (end of JPEG) sequence. 89 | 90 | void searchPicture(int streamLength, byte[] streamBuffer) { 91 | do { 92 | previous = current; 93 | current = streamBuffer[idx++]; 94 | 95 | // JPEG picture start ? 96 | if(previous == picMarker && current == picStart) { 97 | frameIdx = 2; 98 | frameBuffer[0] = picMarker; 99 | frameBuffer[1] = picStart; 100 | inPicture = true; 101 | return; 102 | } 103 | } while(idx < streamLength); 104 | } 105 | 106 | // While we are parsing a picture, fill the frame buffer until a FFD9 is reach. 107 | 108 | void parsePicture(int streamLength, byte[] streamBuffer) { 109 | do { 110 | previous = current; 111 | current = streamBuffer[idx++]; 112 | frameBuffer[frameIdx++] = current; 113 | 114 | // JPEG picture end ? 115 | if(previous == picMarker && current == picEnd) { 116 | Image img = null; 117 | 118 | // Using a memorystream this way prevent arrays copy and allocations 119 | using(var s = new MemoryStream(frameBuffer, 0, frameIdx)) { 120 | try { 121 | img = Image.FromStream(s); 122 | } 123 | catch { 124 | // We dont care about badly decoded pictures 125 | } 126 | } 127 | 128 | // Defer the image processing to prevent slow down 129 | // The image processing delegate must dispose the image eventually. 130 | action(img); 131 | inPicture = false; 132 | return; 133 | } 134 | } while(idx < streamLength); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/MainForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | True 122 | 123 | 124 | 17, 17 125 | 126 | 127 | 128 | 129 | iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 130 | YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGxSURBVEhL7VTNSsNAGOzBP9SD6COI6CMIWj0o4oOUeMkP 131 | SZo26CE3Eb0IliAogoK3Uopv4KFSEHyVWo8qxJnwUZvN1tpbDx0YkuzMfLv7ZZPCBOOPKIpmyuXyFngE 132 | npBBEBgcMwxjWmyjw/O8DRS6BbtgMoBdTHSD67rEhsOyrFkELsBvKTKUpml+YaJz7lbK6BGG4QoCbbUA 133 | WK9Wq9soMEfiuQg2+vSUmKTluu6ylMsCwUWY3jQhTyw5QCcyft/3X8EFsfwC4qNqBuvUZNUxWvGBK99J 134 | ja2UXBNUc/fUesAqDzWmhG2hjvtY1ZC5oobV7qia6PvUU2BA1/eEK6cuK89otm2/U4NnXtWEL9RT4OHP 135 | CXCvO6odav+aANs50BjIInVca8o4W3Ap2q6qCfeo94DAg8bUoMYXyp6zLRjrsDhWnp55PD/1+VNCv6OW 136 | AY8Wj5hqBgOx5IBCoeIl22ybWLLgR4JQSxNq8rQwKD1nW3IrB58dx1mScnpw65jkjJ+/psAgfiJzOtLP 137 | D6E18Fr6ritKdsAYxVclNjqwo6lKpbKJQiXwWFjiGDWxTTC2KBR+ALfrFcqO/pU8AAAAAElFTkSuQmCC 138 | 139 | 140 | 141 | 122, 17 142 | 143 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/SimpleMJPEGStreamViewer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FE05CD86-48CA-47AD-9CAD-5F5E93EDC377} 8 | WinExe 9 | Properties 10 | SimpleMJPEGStreamViewer 11 | SimpleMJPEGStreamViewer 12 | v4.6.1 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | true 27 | 28 | 29 | x64 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | false 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | app.manifest 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll 54 | True 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Component 69 | 70 | 71 | 72 | Component 73 | 74 | 75 | Form 76 | 77 | 78 | MainForm.cs 79 | 80 | 81 | Component 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | MainForm.cs 90 | 91 | 92 | ResXFileCodeGenerator 93 | Resources.Designer.cs 94 | Designer 95 | 96 | 97 | True 98 | Resources.resx 99 | True 100 | 101 | 102 | 103 | 104 | SettingsSingleFileGenerator 105 | Settings.Designer.cs 106 | 107 | 108 | True 109 | Settings.settings 110 | True 111 | 112 | 113 | 114 | 115 | Designer 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | PreserveNewest 187 | 188 | 189 | 190 | 191 | 198 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Ce code a été généré par un outil. 4 | // Version du runtime :4.0.30319.42000 5 | // 6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si 7 | // le code est régénéré. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SimpleMJPEGStreamViewer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. 17 | /// 18 | // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder 19 | // à l'aide d'un outil, tel que ResGen ou Visual Studio. 20 | // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen 21 | // avec l'option /str ou régénérez votre projet VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SimpleMJPEGStreamViewer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Remplace la propriété CurrentUICulture du thread actuel pour toutes 51 | /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap add { 67 | get { 68 | object obj = ResourceManager.GetObject("add", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap cam { 77 | get { 78 | object obj = ResourceManager.GetObject("cam", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap delete { 87 | get { 88 | object obj = ResourceManager.GetObject("delete", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap duplicate { 97 | get { 98 | object obj = ResourceManager.GetObject("duplicate", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 105 | /// 106 | internal static System.Drawing.Bitmap gradient { 107 | get { 108 | object obj = ResourceManager.GetObject("gradient", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 115 | /// 116 | internal static System.Drawing.Bitmap next { 117 | get { 118 | object obj = ResourceManager.GetObject("next", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | 123 | /// 124 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 125 | /// 126 | internal static System.Drawing.Bitmap notready { 127 | get { 128 | object obj = ResourceManager.GetObject("notready", resourceCulture); 129 | return ((System.Drawing.Bitmap)(obj)); 130 | } 131 | } 132 | 133 | /// 134 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 135 | /// 136 | internal static System.Drawing.Bitmap notvisible { 137 | get { 138 | object obj = ResourceManager.GetObject("notvisible", resourceCulture); 139 | return ((System.Drawing.Bitmap)(obj)); 140 | } 141 | } 142 | 143 | /// 144 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 145 | /// 146 | internal static System.Drawing.Bitmap pause { 147 | get { 148 | object obj = ResourceManager.GetObject("pause", resourceCulture); 149 | return ((System.Drawing.Bitmap)(obj)); 150 | } 151 | } 152 | 153 | /// 154 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 155 | /// 156 | internal static System.Drawing.Bitmap play { 157 | get { 158 | object obj = ResourceManager.GetObject("play", resourceCulture); 159 | return ((System.Drawing.Bitmap)(obj)); 160 | } 161 | } 162 | 163 | /// 164 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 165 | /// 166 | internal static System.Drawing.Bitmap previous { 167 | get { 168 | object obj = ResourceManager.GetObject("previous", resourceCulture); 169 | return ((System.Drawing.Bitmap)(obj)); 170 | } 171 | } 172 | 173 | /// 174 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 175 | /// 176 | internal static System.Drawing.Bitmap viewselected { 177 | get { 178 | object obj = ResourceManager.GetObject("viewselected", resourceCulture); 179 | return ((System.Drawing.Bitmap)(obj)); 180 | } 181 | } 182 | 183 | /// 184 | /// Recherche une ressource localisée de type System.Drawing.Bitmap. 185 | /// 186 | internal static System.Drawing.Bitmap visible { 187 | get { 188 | object obj = ResourceManager.GetObject("visible", resourceCulture); 189 | return ((System.Drawing.Bitmap)(obj)); 190 | } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\ic_my_library_add_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\notready.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\gradient.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\ic_visibility_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\Resources\ic_visibility_off_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | 137 | ..\Resources\ic_lens_grey_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | 140 | ..\Resources\ic_delete_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 141 | 142 | 143 | ..\Resources\ic_chevron_left_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 144 | 145 | 146 | ..\Resources\ic_chevron_right_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 147 | 148 | 149 | ..\Resources\ic_play_circle_outline_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 150 | 151 | 152 | ..\Resources\ic_pause_circle_outline_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 153 | 154 | 155 | ..\Resources\ic_perm_media_grey600_24dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 156 | 157 | 158 | ..\Resources\cam.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 159 | 160 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/MainForm.cs: -------------------------------------------------------------------------------- 1 | using SimpleMJPEGStreamViewer.Properties; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | using System.Xml.Serialization; 14 | 15 | namespace SimpleMJPEGStreamViewer { 16 | public partial class MainForm : Form { 17 | 18 | readonly SimpleBindingList videoList = new SimpleBindingList(); 19 | readonly SynchronizationContext sync; 20 | 21 | const string defaultCamFile = "default.xml"; 22 | 23 | public MainForm() { 24 | InitializeComponent(); 25 | 26 | this.Icon = Icon.FromHandle(Resources.cam.GetHicon()); 27 | 28 | sync = SynchronizationContext.Current; 29 | 30 | dataGridView1.AutoGenerateColumns = false; 31 | 32 | IEnumerable sel = null; 33 | 34 | dataGridView1.SaveSelection = columnIndex => { 35 | sel = columnIndex == PlayingColumn.Index || columnIndex == VisibleColumn.Index ? dataGridView1.SelectedRows.OfType() : null; 36 | }; 37 | 38 | dataGridView1.RestoreSelection += () => { 39 | if(sel != null) { 40 | foreach(var row in sel) 41 | row.Selected = true; 42 | } 43 | }; 44 | 45 | dataGridView1.SelectionChanged += (s, e) => { 46 | propertyGrid1.SelectedObjects = selectedItems.ToArray(); 47 | }; 48 | 49 | dataGridView1.DataSource = videoList; 50 | videoList.ListChanged += VideoList_ListChanged; 51 | videoList.BeforeRemove += VideoList_BeforeRemove; 52 | 53 | if(File.Exists(defaultCamFile)) { 54 | loadCams(defaultCamFile); 55 | } 56 | 57 | this.FormClosing += (s, e) => { 58 | clearVideoList(); 59 | }; 60 | } 61 | 62 | private void VideoList_BeforeRemove(object sender, VideoItem item) { 63 | item.Playing = false; 64 | var ctl = simpleLayoutPanel1.Controls[item.UUID.ToString()]; 65 | if(ctl != null) 66 | ctl.Dispose(); 67 | } 68 | 69 | void adaptProperties(VideoItem item, string propertyName = null) { 70 | var byPass = propertyName == null; 71 | if(propertyName == "Visible" || byPass) { 72 | simpleLayoutPanel1.Controls[item.UUID.ToString()].Visible = item.Visible; 73 | } 74 | 75 | if(propertyName == "Playing" || byPass) { 76 | if(item.Playing) { 77 | var pb = (SimplePictureBox)simpleLayoutPanel1.Controls[item.UUID.ToString()]; 78 | Task.Run(() => startVideoAsync(pb, item)); 79 | } 80 | } 81 | 82 | if(propertyGrid1.SelectedObjects.Contains(item)) 83 | propertyGrid1.Refresh(); 84 | } 85 | 86 | void VideoList_ListChanged(object sender, ListChangedEventArgs e) { 87 | if(e.ListChangedType == ListChangedType.ItemAdded) { 88 | var item = (VideoItem)((IList)sender)[e.NewIndex]; 89 | var pb = new SimplePictureBox { Name = item.UUID.ToString() }; 90 | simpleLayoutPanel1.Controls.Add(pb); 91 | adaptProperties(item); 92 | } 93 | else if(e.ListChangedType == ListChangedType.ItemChanged) { 94 | var item = (VideoItem)((IList)sender)[e.NewIndex]; 95 | adaptProperties(item, e.PropertyDescriptor.Name); 96 | } 97 | propertyGrid1.Refresh(); 98 | } 99 | 100 | async Task startVideoAsync(SimplePictureBox pb, VideoItem item) { 101 | using(var cts = new CancellationTokenSource()) { 102 | item.Cts = cts; 103 | try { 104 | await SimpleMJPEGDecoder.StartAsync( 105 | // await SimpleMJPEGDecoderDataFlow.StartAsync( 106 | image => { 107 | sync.Post(new SendOrPostCallback(_ => pb.Image = image), null); 108 | }, 109 | item.Url, 110 | item.Login, 111 | item.Password, 112 | cts.Token, 113 | item.MaxStreamBufferSize); 114 | } 115 | catch(OperationCanceledException ex) { 116 | Console.WriteLine(ex); 117 | } 118 | catch(Exception ex) { 119 | Console.WriteLine(ex); 120 | pb.Image = (Image)Properties.Resources.notready.Clone(); 121 | } 122 | finally { 123 | item.Playing = false; 124 | } 125 | } 126 | } 127 | 128 | private void toolStripButton1_Click(object sender, EventArgs e) { 129 | videoList.Add(new VideoItem()); 130 | } 131 | 132 | IEnumerable selectedItems { 133 | get { 134 | return 135 | dataGridView1 136 | .SelectedRows 137 | .OfType() 138 | .Select( 139 | r => { 140 | return (VideoItem)r.DataBoundItem; 141 | }); 142 | } 143 | } 144 | 145 | private void toolStripButton2_Click(object sender, EventArgs e) { 146 | foreach(var item in selectedItems) { 147 | videoList.Remove(item); 148 | } 149 | } 150 | 151 | private void toolStripButton4_Click(object sender, EventArgs e) { 152 | foreach(var item in selectedItems) 153 | item.Playing = false; 154 | } 155 | 156 | private void toolStripButton3_Click(object sender, EventArgs e) { 157 | foreach(var item in selectedItems.Where(v => !v.Playing)) { 158 | item.Playing = true; 159 | } 160 | } 161 | 162 | private void toolStripButton5_Click(object sender, EventArgs e) { 163 | foreach(var item in selectedItems) { 164 | item.Visible = true; 165 | } 166 | } 167 | 168 | private void toolStripButton6_Click(object sender, EventArgs e) { 169 | foreach(var item in selectedItems) 170 | item.Visible = false; 171 | } 172 | 173 | private void toolStripButton7_Click(object sender, EventArgs e) { 174 | foreach(var item in selectedItems) 175 | videoList.Add(new VideoItem { 176 | Visible = item.Visible, 177 | Name = item.Name, 178 | Url = item.Url, 179 | Playing = item.Playing, 180 | Login = item.Login, 181 | Password = item.Password 182 | }); 183 | } 184 | 185 | private void toolStripButton8_Click(object sender, EventArgs e) { 186 | singleView(); 187 | } 188 | 189 | private void singleView() { 190 | foreach(var item in dataGridView1.Rows.OfType().Select(d => new { row = d, vid = (VideoItem)d.DataBoundItem })) 191 | item.vid.Visible = item.row.Selected; 192 | } 193 | 194 | private void toolStripButton9_Click(object sender, EventArgs e) { 195 | var row = dataGridView1.SelectedRows.OfType().FirstOrDefault(); 196 | if(row == null) 197 | return; 198 | 199 | var nextRow = dataGridView1.Rows.GetPreviousRow(row.Index, DataGridViewElementStates.None); 200 | if(nextRow == -1) 201 | return; 202 | 203 | dataGridView1.ClearSelection(); 204 | dataGridView1.Rows[nextRow].Selected = true; 205 | 206 | foreach(var item in dataGridView1.Rows.OfType().Select(d => new { row = d, vid = (VideoItem)d.DataBoundItem })) 207 | item.vid.Visible = item.row.Selected; 208 | } 209 | 210 | private void toolStripButton10_Click(object sender, EventArgs e) { 211 | var row = dataGridView1.SelectedRows.OfType().FirstOrDefault(); 212 | if(row == null) 213 | return; 214 | 215 | var nextRow = dataGridView1.Rows.GetNextRow(row.Index, DataGridViewElementStates.None); 216 | if(nextRow == -1) 217 | return; 218 | 219 | dataGridView1.ClearSelection(); 220 | dataGridView1.Rows[nextRow].Selected = true; 221 | 222 | foreach(var item in dataGridView1.Rows.OfType().Select(d => new { row = d, vid = (VideoItem)d.DataBoundItem })) 223 | item.vid.Visible = item.row.Selected; 224 | } 225 | 226 | private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { 227 | if(e.ColumnIndex == PlayingColumn.Index) { 228 | e.Value = (bool)e.Value ? "4" : ";"; 229 | e.FormattingApplied = true; 230 | } 231 | else if(e.ColumnIndex == VisibleColumn.Index) { 232 | e.Value = (bool)e.Value ? "ü" : "û"; 233 | e.FormattingApplied = true; 234 | } 235 | } 236 | 237 | private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { 238 | if((e.ColumnIndex == PlayingColumn.Index || e.ColumnIndex == VisibleColumn.Index) && e.RowIndex != -1) { 239 | var value = (bool)((DataGridView)sender)[e.ColumnIndex, e.RowIndex].Value; 240 | foreach(var sel in dataGridView1.SelectedRows.OfType()) { 241 | sel.Cells[e.ColumnIndex].Value = !value; 242 | } 243 | } 244 | } 245 | 246 | static readonly XmlSerializer s = new XmlSerializer(typeof(VideoItem[])); 247 | 248 | private void saveToolStripMenuItem_Click(object sender, EventArgs e) { 249 | using(var fileSave = new SaveFileDialog()) { 250 | if(fileSave.ShowDialog() == DialogResult.OK) { 251 | using(var f = new FileStream(fileSave.FileName, FileMode.Create)) 252 | s.Serialize(f, videoList.ToArray()); 253 | } 254 | } 255 | } 256 | 257 | private void loadToolStripMenuItem_Click(object sender, EventArgs e) { 258 | using(var fileLoad = new OpenFileDialog()) { 259 | if(fileLoad.ShowDialog() == DialogResult.OK) { 260 | var camFile = fileLoad.FileName; 261 | loadCams(camFile); 262 | } 263 | } 264 | } 265 | 266 | void clearVideoList() { 267 | foreach(var item in videoList.ToList()) { 268 | videoList.Remove(item); 269 | } 270 | } 271 | 272 | private void loadCams(string camFile) { 273 | using(var f = new FileStream(camFile, FileMode.Open)) { 274 | var loadedItems = (VideoItem[])s.Deserialize(f); 275 | clearVideoList(); 276 | foreach(var item in loadedItems) { 277 | videoList.Add(item); 278 | } 279 | } 280 | } 281 | 282 | private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { 283 | singleView(); 284 | } 285 | 286 | private void exitToolStripMenuItem_Click(object sender, EventArgs e) { 287 | this.Close(); 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /SimpleMJPEGStreamViewer/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleMJPEGStreamViewer { 2 | partial class MainForm { 3 | /// 4 | /// Variable nécessaire au concepteur. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Nettoyage des ressources utilisées. 10 | /// 11 | /// true si les ressources managées doivent être supprimées ; sinon, false. 12 | protected override void Dispose(bool disposing) { 13 | if(disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Code généré par le Concepteur Windows Form 20 | 21 | /// 22 | /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas 23 | /// le contenu de cette méthode avec l'éditeur de code. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); 27 | System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); 28 | System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); 29 | this.splitContainer1 = new System.Windows.Forms.SplitContainer(); 30 | this.splitContainer2 = new System.Windows.Forms.SplitContainer(); 31 | this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); 32 | this.toolStrip1 = new System.Windows.Forms.ToolStrip(); 33 | this.toolStripButton9 = new System.Windows.Forms.ToolStripButton(); 34 | this.toolStripButton10 = new System.Windows.Forms.ToolStripButton(); 35 | this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); 36 | this.toolStripButton8 = new System.Windows.Forms.ToolStripButton(); 37 | this.toolStripButton5 = new System.Windows.Forms.ToolStripButton(); 38 | this.toolStripButton6 = new System.Windows.Forms.ToolStripButton(); 39 | this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); 40 | this.toolStripButton3 = new System.Windows.Forms.ToolStripButton(); 41 | this.toolStripButton4 = new System.Windows.Forms.ToolStripButton(); 42 | this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); 43 | this.toolStripButton2 = new System.Windows.Forms.ToolStripButton(); 44 | this.toolStripButton1 = new System.Windows.Forms.ToolStripButton(); 45 | this.toolStripButton7 = new System.Windows.Forms.ToolStripButton(); 46 | this.menuStrip1 = new System.Windows.Forms.MenuStrip(); 47 | this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 48 | this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 49 | this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 50 | this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); 51 | this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 52 | this.dataGridView1 = new SimpleMJPEGStreamViewer.SimpleDataGridView(); 53 | this.NameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); 54 | this.VisibleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); 55 | this.PlayingColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); 56 | this.simpleLayoutPanel1 = new SimpleMJPEGStreamViewer.SimpleLayoutPanel(); 57 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); 58 | this.splitContainer1.Panel1.SuspendLayout(); 59 | this.splitContainer1.Panel2.SuspendLayout(); 60 | this.splitContainer1.SuspendLayout(); 61 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); 62 | this.splitContainer2.Panel1.SuspendLayout(); 63 | this.splitContainer2.Panel2.SuspendLayout(); 64 | this.splitContainer2.SuspendLayout(); 65 | this.toolStrip1.SuspendLayout(); 66 | this.menuStrip1.SuspendLayout(); 67 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); 68 | this.SuspendLayout(); 69 | // 70 | // splitContainer1 71 | // 72 | this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 73 | this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; 74 | this.splitContainer1.Location = new System.Drawing.Point(0, 70); 75 | this.splitContainer1.Name = "splitContainer1"; 76 | // 77 | // splitContainer1.Panel1 78 | // 79 | this.splitContainer1.Panel1.Controls.Add(this.splitContainer2); 80 | // 81 | // splitContainer1.Panel2 82 | // 83 | this.splitContainer1.Panel2.Controls.Add(this.simpleLayoutPanel1); 84 | this.splitContainer1.Size = new System.Drawing.Size(1066, 464); 85 | this.splitContainer1.SplitterDistance = 321; 86 | this.splitContainer1.TabIndex = 0; 87 | // 88 | // splitContainer2 89 | // 90 | this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; 91 | this.splitContainer2.Location = new System.Drawing.Point(0, 0); 92 | this.splitContainer2.Name = "splitContainer2"; 93 | this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; 94 | // 95 | // splitContainer2.Panel1 96 | // 97 | this.splitContainer2.Panel1.Controls.Add(this.dataGridView1); 98 | // 99 | // splitContainer2.Panel2 100 | // 101 | this.splitContainer2.Panel2.Controls.Add(this.propertyGrid1); 102 | this.splitContainer2.Size = new System.Drawing.Size(319, 462); 103 | this.splitContainer2.SplitterDistance = 329; 104 | this.splitContainer2.TabIndex = 1; 105 | // 106 | // propertyGrid1 107 | // 108 | this.propertyGrid1.CommandsVisibleIfAvailable = false; 109 | this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; 110 | this.propertyGrid1.HelpVisible = false; 111 | this.propertyGrid1.Location = new System.Drawing.Point(0, 0); 112 | this.propertyGrid1.Name = "propertyGrid1"; 113 | this.propertyGrid1.Size = new System.Drawing.Size(319, 129); 114 | this.propertyGrid1.TabIndex = 0; 115 | this.propertyGrid1.ToolbarVisible = false; 116 | // 117 | // toolStrip1 118 | // 119 | this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; 120 | this.toolStrip1.ImageScalingSize = new System.Drawing.Size(24, 24); 121 | this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 122 | this.toolStripButton9, 123 | this.toolStripButton10, 124 | this.toolStripSeparator3, 125 | this.toolStripButton5, 126 | this.toolStripButton6, 127 | this.toolStripButton8, 128 | this.toolStripSeparator2, 129 | this.toolStripButton3, 130 | this.toolStripButton4, 131 | this.toolStripSeparator1, 132 | this.toolStripButton2, 133 | this.toolStripButton1, 134 | this.toolStripButton7}); 135 | this.toolStrip1.Location = new System.Drawing.Point(0, 24); 136 | this.toolStrip1.Name = "toolStrip1"; 137 | this.toolStrip1.Size = new System.Drawing.Size(1066, 46); 138 | this.toolStrip1.TabIndex = 0; 139 | this.toolStrip1.Text = "toolStrip1"; 140 | // 141 | // toolStripButton9 142 | // 143 | this.toolStripButton9.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.previous; 144 | this.toolStripButton9.ImageTransparentColor = System.Drawing.Color.Magenta; 145 | this.toolStripButton9.Name = "toolStripButton9"; 146 | this.toolStripButton9.Size = new System.Drawing.Size(34, 43); 147 | this.toolStripButton9.Text = "Prev"; 148 | this.toolStripButton9.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 149 | this.toolStripButton9.Click += new System.EventHandler(this.toolStripButton9_Click); 150 | // 151 | // toolStripButton10 152 | // 153 | this.toolStripButton10.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.next; 154 | this.toolStripButton10.ImageTransparentColor = System.Drawing.Color.Magenta; 155 | this.toolStripButton10.Name = "toolStripButton10"; 156 | this.toolStripButton10.Size = new System.Drawing.Size(35, 43); 157 | this.toolStripButton10.Text = "Next"; 158 | this.toolStripButton10.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 159 | this.toolStripButton10.Click += new System.EventHandler(this.toolStripButton10_Click); 160 | // 161 | // toolStripSeparator3 162 | // 163 | this.toolStripSeparator3.Name = "toolStripSeparator3"; 164 | this.toolStripSeparator3.Size = new System.Drawing.Size(6, 46); 165 | // 166 | // toolStripButton8 167 | // 168 | this.toolStripButton8.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.viewselected; 169 | this.toolStripButton8.ImageTransparentColor = System.Drawing.Color.Magenta; 170 | this.toolStripButton8.Name = "toolStripButton8"; 171 | this.toolStripButton8.Size = new System.Drawing.Size(55, 43); 172 | this.toolStripButton8.Text = "Selected"; 173 | this.toolStripButton8.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 174 | this.toolStripButton8.Click += new System.EventHandler(this.toolStripButton8_Click); 175 | // 176 | // toolStripButton5 177 | // 178 | this.toolStripButton5.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton5.Image"))); 179 | this.toolStripButton5.ImageTransparentColor = System.Drawing.Color.Magenta; 180 | this.toolStripButton5.Name = "toolStripButton5"; 181 | this.toolStripButton5.Size = new System.Drawing.Size(40, 43); 182 | this.toolStripButton5.Text = "Show"; 183 | this.toolStripButton5.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 184 | this.toolStripButton5.Click += new System.EventHandler(this.toolStripButton5_Click); 185 | // 186 | // toolStripButton6 187 | // 188 | this.toolStripButton6.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.notvisible; 189 | this.toolStripButton6.ImageTransparentColor = System.Drawing.Color.Magenta; 190 | this.toolStripButton6.Name = "toolStripButton6"; 191 | this.toolStripButton6.Size = new System.Drawing.Size(36, 43); 192 | this.toolStripButton6.Text = "Hide"; 193 | this.toolStripButton6.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 194 | this.toolStripButton6.Click += new System.EventHandler(this.toolStripButton6_Click); 195 | // 196 | // toolStripSeparator2 197 | // 198 | this.toolStripSeparator2.Name = "toolStripSeparator2"; 199 | this.toolStripSeparator2.Size = new System.Drawing.Size(6, 46); 200 | // 201 | // toolStripButton3 202 | // 203 | this.toolStripButton3.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.play; 204 | this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta; 205 | this.toolStripButton3.Name = "toolStripButton3"; 206 | this.toolStripButton3.Size = new System.Drawing.Size(33, 43); 207 | this.toolStripButton3.Text = "Play"; 208 | this.toolStripButton3.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 209 | this.toolStripButton3.Click += new System.EventHandler(this.toolStripButton3_Click); 210 | // 211 | // toolStripButton4 212 | // 213 | this.toolStripButton4.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.pause; 214 | this.toolStripButton4.ImageTransparentColor = System.Drawing.Color.Magenta; 215 | this.toolStripButton4.Name = "toolStripButton4"; 216 | this.toolStripButton4.Size = new System.Drawing.Size(35, 43); 217 | this.toolStripButton4.Text = "Stop"; 218 | this.toolStripButton4.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 219 | this.toolStripButton4.Click += new System.EventHandler(this.toolStripButton4_Click); 220 | // 221 | // toolStripSeparator1 222 | // 223 | this.toolStripSeparator1.Name = "toolStripSeparator1"; 224 | this.toolStripSeparator1.Size = new System.Drawing.Size(6, 46); 225 | // 226 | // toolStripButton2 227 | // 228 | this.toolStripButton2.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.delete; 229 | this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta; 230 | this.toolStripButton2.Name = "toolStripButton2"; 231 | this.toolStripButton2.Size = new System.Drawing.Size(54, 43); 232 | this.toolStripButton2.Text = "Remove"; 233 | this.toolStripButton2.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 234 | this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click); 235 | // 236 | // toolStripButton1 237 | // 238 | this.toolStripButton1.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.add; 239 | this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta; 240 | this.toolStripButton1.Name = "toolStripButton1"; 241 | this.toolStripButton1.Size = new System.Drawing.Size(33, 43); 242 | this.toolStripButton1.Text = "Add"; 243 | this.toolStripButton1.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 244 | this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click); 245 | // 246 | // toolStripButton7 247 | // 248 | this.toolStripButton7.Image = global::SimpleMJPEGStreamViewer.Properties.Resources.duplicate; 249 | this.toolStripButton7.ImageTransparentColor = System.Drawing.Color.Magenta; 250 | this.toolStripButton7.Name = "toolStripButton7"; 251 | this.toolStripButton7.Size = new System.Drawing.Size(42, 43); 252 | this.toolStripButton7.Text = "Clone"; 253 | this.toolStripButton7.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; 254 | this.toolStripButton7.Click += new System.EventHandler(this.toolStripButton7_Click); 255 | // 256 | // menuStrip1 257 | // 258 | this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 259 | this.fileToolStripMenuItem}); 260 | this.menuStrip1.Location = new System.Drawing.Point(0, 0); 261 | this.menuStrip1.Name = "menuStrip1"; 262 | this.menuStrip1.Size = new System.Drawing.Size(1066, 24); 263 | this.menuStrip1.TabIndex = 1; 264 | this.menuStrip1.Text = "menuStrip1"; 265 | // 266 | // fileToolStripMenuItem 267 | // 268 | this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 269 | this.loadToolStripMenuItem, 270 | this.saveToolStripMenuItem, 271 | this.toolStripSeparator4, 272 | this.exitToolStripMenuItem}); 273 | this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; 274 | this.fileToolStripMenuItem.Size = new System.Drawing.Size(78, 20); 275 | this.fileToolStripMenuItem.Text = "Camera list"; 276 | // 277 | // loadToolStripMenuItem 278 | // 279 | this.loadToolStripMenuItem.Name = "loadToolStripMenuItem"; 280 | this.loadToolStripMenuItem.Size = new System.Drawing.Size(152, 22); 281 | this.loadToolStripMenuItem.Text = "&Load"; 282 | this.loadToolStripMenuItem.Click += new System.EventHandler(this.loadToolStripMenuItem_Click); 283 | // 284 | // saveToolStripMenuItem 285 | // 286 | this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; 287 | this.saveToolStripMenuItem.Size = new System.Drawing.Size(152, 22); 288 | this.saveToolStripMenuItem.Text = "&Save"; 289 | this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click); 290 | // 291 | // toolStripSeparator4 292 | // 293 | this.toolStripSeparator4.Name = "toolStripSeparator4"; 294 | this.toolStripSeparator4.Size = new System.Drawing.Size(149, 6); 295 | // 296 | // exitToolStripMenuItem 297 | // 298 | this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; 299 | this.exitToolStripMenuItem.Size = new System.Drawing.Size(152, 22); 300 | this.exitToolStripMenuItem.Text = "&Exit"; 301 | this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); 302 | // 303 | // dataGridView1 304 | // 305 | this.dataGridView1.AllowUserToAddRows = false; 306 | this.dataGridView1.AllowUserToDeleteRows = false; 307 | this.dataGridView1.AllowUserToResizeRows = false; 308 | this.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells; 309 | this.dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None; 310 | this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 311 | this.dataGridView1.ColumnHeadersVisible = false; 312 | this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { 313 | this.NameColumn, 314 | this.VisibleColumn, 315 | this.PlayingColumn}); 316 | this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; 317 | this.dataGridView1.Location = new System.Drawing.Point(0, 0); 318 | this.dataGridView1.Name = "dataGridView1"; 319 | this.dataGridView1.ReadOnly = true; 320 | this.dataGridView1.RowHeadersVisible = false; 321 | this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; 322 | this.dataGridView1.Size = new System.Drawing.Size(319, 329); 323 | this.dataGridView1.TabIndex = 0; 324 | this.dataGridView1.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellClick); 325 | this.dataGridView1.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellDoubleClick); 326 | this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView1_CellFormatting); 327 | // 328 | // NameColumn 329 | // 330 | this.NameColumn.DataPropertyName = "Name"; 331 | this.NameColumn.HeaderText = "Column1"; 332 | this.NameColumn.Name = "NameColumn"; 333 | this.NameColumn.ReadOnly = true; 334 | this.NameColumn.Width = 5; 335 | // 336 | // VisibleColumn 337 | // 338 | this.VisibleColumn.DataPropertyName = "Visible"; 339 | dataGridViewCellStyle1.Font = new System.Drawing.Font("Wingdings", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2))); 340 | this.VisibleColumn.DefaultCellStyle = dataGridViewCellStyle1; 341 | this.VisibleColumn.HeaderText = "Column2"; 342 | this.VisibleColumn.Name = "VisibleColumn"; 343 | this.VisibleColumn.ReadOnly = true; 344 | this.VisibleColumn.Width = 5; 345 | // 346 | // PlayingColumn 347 | // 348 | this.PlayingColumn.DataPropertyName = "Playing"; 349 | dataGridViewCellStyle2.Font = new System.Drawing.Font("Webdings", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2))); 350 | this.PlayingColumn.DefaultCellStyle = dataGridViewCellStyle2; 351 | this.PlayingColumn.HeaderText = "Column1"; 352 | this.PlayingColumn.Name = "PlayingColumn"; 353 | this.PlayingColumn.ReadOnly = true; 354 | this.PlayingColumn.Width = 5; 355 | // 356 | // simpleLayoutPanel1 357 | // 358 | this.simpleLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; 359 | this.simpleLayoutPanel1.Location = new System.Drawing.Point(0, 0); 360 | this.simpleLayoutPanel1.Name = "simpleLayoutPanel1"; 361 | this.simpleLayoutPanel1.Size = new System.Drawing.Size(739, 462); 362 | this.simpleLayoutPanel1.TabIndex = 0; 363 | // 364 | // MainForm 365 | // 366 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 367 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 368 | this.ClientSize = new System.Drawing.Size(1066, 534); 369 | this.Controls.Add(this.splitContainer1); 370 | this.Controls.Add(this.toolStrip1); 371 | this.Controls.Add(this.menuStrip1); 372 | this.DoubleBuffered = true; 373 | this.MainMenuStrip = this.menuStrip1; 374 | this.Name = "MainForm"; 375 | this.Text = "Simple MJPEG Stream Viewer"; 376 | this.splitContainer1.Panel1.ResumeLayout(false); 377 | this.splitContainer1.Panel2.ResumeLayout(false); 378 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); 379 | this.splitContainer1.ResumeLayout(false); 380 | this.splitContainer2.Panel1.ResumeLayout(false); 381 | this.splitContainer2.Panel2.ResumeLayout(false); 382 | ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); 383 | this.splitContainer2.ResumeLayout(false); 384 | this.toolStrip1.ResumeLayout(false); 385 | this.toolStrip1.PerformLayout(); 386 | this.menuStrip1.ResumeLayout(false); 387 | this.menuStrip1.PerformLayout(); 388 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); 389 | this.ResumeLayout(false); 390 | this.PerformLayout(); 391 | 392 | } 393 | 394 | #endregion 395 | 396 | private System.Windows.Forms.SplitContainer splitContainer1; 397 | private System.Windows.Forms.ToolStrip toolStrip1; 398 | private System.Windows.Forms.ToolStripButton toolStripButton1; 399 | private System.Windows.Forms.ToolStripButton toolStripButton2; 400 | private System.Windows.Forms.SplitContainer splitContainer2; 401 | private System.Windows.Forms.PropertyGrid propertyGrid1; 402 | private System.Windows.Forms.ToolStripButton toolStripButton3; 403 | private System.Windows.Forms.ToolStripButton toolStripButton4; 404 | private System.Windows.Forms.ToolStripButton toolStripButton5; 405 | private System.Windows.Forms.ToolStripButton toolStripButton6; 406 | private System.Windows.Forms.ToolStripButton toolStripButton7; 407 | private SimpleDataGridView dataGridView1; 408 | private System.Windows.Forms.ToolStripButton toolStripButton8; 409 | private SimpleLayoutPanel simpleLayoutPanel1; 410 | private System.Windows.Forms.ToolStripButton toolStripButton9; 411 | private System.Windows.Forms.ToolStripButton toolStripButton10; 412 | private System.Windows.Forms.DataGridViewTextBoxColumn NameColumn; 413 | private System.Windows.Forms.DataGridViewTextBoxColumn VisibleColumn; 414 | private System.Windows.Forms.DataGridViewTextBoxColumn PlayingColumn; 415 | private System.Windows.Forms.MenuStrip menuStrip1; 416 | private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; 417 | private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem; 418 | private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem; 419 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; 420 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; 421 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; 422 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; 423 | private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; 424 | } 425 | } 426 | 427 | --------------------------------------------------------------------------------