<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kouji Matsui</title>
    <description>The latest articles on Forem by Kouji Matsui (@kozy_kekyo).</description>
    <link>https://forem.com/kozy_kekyo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F43079%2Fcb0858a1-3549-49e7-816b-b5ad126e80b1.png</url>
      <title>Forem: Kouji Matsui</title>
      <link>https://forem.com/kozy_kekyo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kozy_kekyo"/>
    <language>en</language>
    <item>
      <title>MapLibre GL JS - Enabling large number of moveable sprites</title>
      <dc:creator>Kouji Matsui</dc:creator>
      <pubDate>Wed, 03 Dec 2025 08:31:15 +0000</pubDate>
      <link>https://forem.com/kozy_kekyo/maplibre-gl-js-enabling-large-number-of-moveable-sprites-4bek</link>
      <guid>https://forem.com/kozy_kekyo/maplibre-gl-js-enabling-large-number-of-moveable-sprites-4bek</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/kekyo/maplibre-gl-layers" rel="noopener noreferrer"&gt;maplibre-gl-layers&lt;/a&gt; reached 1.0.0 🎉&lt;/p&gt;

&lt;p&gt;MapLibre's layer extension library enabling the display, movement, and modification of large numbers of dynamic sprite images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvv375py2gxwmpj2ygwke.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvv375py2gxwmpj2ygwke.jpg" alt=" " width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Place, update, and remove large numbers of sprites.&lt;/li&gt;
&lt;li&gt;Move each sprite's coordinate freely, making it easy to represent moving objects.&lt;/li&gt;
&lt;li&gt;Specify per-sprite anchor positions for precise rendering.&lt;/li&gt;
&lt;li&gt;Add multiple images and text to the same sprite, adjusting rotation, offset, scale, opacity, and more.&lt;/li&gt;
&lt;li&gt;Animate sprite movement, rotation, and offsets with interpolation controls.&lt;/li&gt;
&lt;li&gt;Control draw order via sub-layers and per-sprite ordering.&lt;/li&gt;
&lt;li&gt;Fully imperative APIs. Updates with high-performance and extensible.&lt;/li&gt;
&lt;li&gt;Accelerating computational processing with WASM and shaders.&lt;/li&gt;
&lt;li&gt;MIT license.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>maplibre</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Easy to implement video image capture with FlashCap</title>
      <dc:creator>Kouji Matsui</dc:creator>
      <pubDate>Sat, 23 Apr 2022 12:19:52 +0000</pubDate>
      <link>https://forem.com/kozy_kekyo/easy-to-implement-video-image-capture-with-flashcap-o5a</link>
      <guid>https://forem.com/kozy_kekyo/easy-to-implement-video-image-capture-with-flashcap-o5a</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wanted to take a video captured image and add your own custom operations to it? Have you ever looked for a library that makes it easy to implement such custom operations?&lt;/p&gt;

&lt;p&gt;Applications that directly manipulate physical devices, such as video capture devices, create restrictions on the libraries that can be used for such operations. .NET, you need to attach native interoperability libraries. This is not a problem for hobby projects that you use only by yourself, but when you get to the stage of creating a package of applications and libraries for someone else to use, a problem emerges. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native libraries need to be compiled in C++ or similar for each of their platforms.
For Windows, you may need to have separate ones for 32-bit and 64-bit, and possibly for the ARM platform in the near future.
For Linux, the number of target platforms will further increase.&lt;/li&gt;
&lt;li&gt;Packaging native libraries requires special knowledge.
There may be a need to place library files in the proper locations and possibly set up custom code to load them dynamically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Jan 2022, my client's requirements drove the need for a camera device on Windows to take still images and perform image processing and image recognition. My initial assumption was to use a USB-connected camera (a device that supports USB Video Class) to take the still image. I wrote "still images," but in reality we also need to preview the images, so they need to be handled as video stream.&lt;/p&gt;

&lt;p&gt;If we just want to record video, there are excellent libraries, applications, and UI controls for recording video. But at the same time, we needed such a library that would be customizable to do its own processing in the background for a single still image during the video preview, and that would have an easy and simple interface and clear packaging issues. (Sometimes referred to as "A frame grabber" as a general term.)&lt;/p&gt;

&lt;p&gt;If we don't have it, let's make it. This is how &lt;a href="https://github.com/kekyo/FlashCap" rel="noopener noreferrer"&gt;FlashCap&lt;/a&gt; was born.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;FlashCap&lt;/code&gt; was born out of work, but is a fully OSS project. License follows &lt;code&gt;Apache-v2&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;My goal was to solve the following problems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The interface structure should be easy enough for anyone to write code.&lt;/li&gt;
&lt;li&gt;Extensibility to allow very low-level operations if necessary.&lt;/li&gt;
&lt;li&gt;Support a wide range of platforms.&lt;/li&gt;
&lt;li&gt;The higher the .NET compatibility, the better.&lt;/li&gt;
&lt;li&gt;Not dependent on other native and/or managed libraries as much as possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For 3, as of the official version (1.0.0):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows (DirectShow API)&lt;/li&gt;
&lt;li&gt;Windows (Video For Windows API)&lt;/li&gt;
&lt;li&gt;Linux (V4L2 API)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;are now supported. (Perhaps it can't be called multi-platform unless it supports more environments ;) For now, my personal interest is in the Android NDK.)&lt;/p&gt;

&lt;p&gt;In addition, the following PCs have been verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows (x86, x64)&lt;/li&gt;
&lt;li&gt;Linux (i686, x86_64, aarch64, armv7l, mipsel)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have verified with &lt;a href="https://github.com/kekyo/FlashCap#tested-devices" rel="noopener noreferrer"&gt;various cameras and video capture devices.&lt;/a&gt; Not all combinations have been covered, but we believe this is sufficient for a first version. For a detailed list, please refer to the repository documentation.&lt;/p&gt;

&lt;p&gt;4 also supports the current major versions of .NET (.NET 6,5, Core 3, Core 2, Framework 4,3, and Standard). If you have ever struggled with package dependencies, you will find comfort in the fact that it is so comprehensive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;So how "easy" is it to write code? First, install the FlashCap package from &lt;a href="https://www.nuget.org/packages/FlashCap" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;. An example code is shown below. It lists the video capture devices and what characteristics (resolution, FPS, format) each device has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FlashCap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Capture device enumeration:&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;devices&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CaptureDevices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnumerateDescriptors&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// "Logicool Webcam C930e: DirectShow device, Characteristics=34"&lt;/span&gt;
    &lt;span class="c1"&gt;// "Default: VideoForWindows default, Characteristics=1"&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;characteristics&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Characteristics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// "1920x1080 [JPEG, 30fps]"&lt;/span&gt;
        &lt;span class="c1"&gt;// "640x480 [YUYV, 60fps]"&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;characteristics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no need to determine the operating environment separately for Windows and Linux. It will automatically determine the environment internally and use the appropriate API.&lt;/p&gt;

&lt;p&gt;Once you have narrowed down the device and its characteristics, all you have to do is open the device and start capturing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Open a device with a video characteristics:&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;descriptor0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnumerateDescriptors&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ElementAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;descriptor0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;descriptor0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Characteristics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;bufferScope&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Captured into a pixel buffer from an argument.&lt;/span&gt;

    &lt;span class="c1"&gt;// Get image data (Maybe DIB/Jpeg/PNG):&lt;/span&gt;
    &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;imageData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bufferScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExtractImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Anything use of it...&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bitmap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Drawing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Start processing:&lt;/span&gt;
&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="c1"&gt;// Stop processing:&lt;/span&gt;
&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the handler for the lambda expression block specified in the &lt;code&gt;OpenAsync()&lt;/code&gt; argument is called each time a capture is made. From the &lt;code&gt;bufferScope.Buffer&lt;/code&gt; argument (called the pixel buffer), &lt;code&gt;ExtractImage()&lt;/code&gt; will yield a &lt;code&gt;byte[]&lt;/code&gt; representing the image data. As in this code example, you can pass it to &lt;code&gt;System.Drawing.Bitmap&lt;/code&gt; to generate a bitmap or output it directly to a file, and that completes the capture process.&lt;/p&gt;

&lt;p&gt;The image data can be in the following format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'RGB DIB': a bitmap file format such as the common &lt;code&gt;foo.bmp&lt;/code&gt;.
The format can vary from 32-bit ARGB to 8-bit palette, but current capture device specifications are almost always 24-bit or 32-bit.&lt;/li&gt;
&lt;li&gt;'JPEG': JPEG format. Most of the time it is actually MJPEG (Motion JPEG), but since it is actually identical to JPEG, it can be treated as JPEG.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are familiar with the video capture situation, you may wonder what happens to the 'YUV' format.' The 'YUV' format is supported by many capture devices, but is a minor player in image data. Many bitmap decoders cannot decode the 'YUV' format. So, &lt;code&gt;FlashCap&lt;/code&gt; will &lt;code&gt;transcode&lt;/code&gt; 'YUV' to 'RGB DIB' by default. Of course, if you need raw image data, you can disable transcoding.&lt;/p&gt;

&lt;p&gt;By the way, &lt;code&gt;FlashCap&lt;/code&gt; handles video. It is not a still picture. Therefore, the above handler is called each time a frame (one still image) of a video is obtained. So, if you want a snapshot, write the code to save one still image at that timing. For example, you could always store the last image in the member field, and then save a reference to it on a click of a button UI, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topic
&lt;/h2&gt;

&lt;p&gt;It should also be noted that the above handlers are called from the worker thread; WPF, Windows Forms, and other UI frameworks do not allow manipulation of the UI from the worker thread. For example, you may want to preview the resulting image data. Each of them has its own specific method. In the case of WPF, you can use a &lt;code&gt;Dispatcher&lt;/code&gt; and write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Image previewImage;&lt;/span&gt;

&lt;span class="c1"&gt;// here executed in a worker thread&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;imageData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bufferScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExtractImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bitmap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BitmapImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CacheOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitmapCacheOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnLoad&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Freeze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Freeze is necessary because it was generated in a worker thread&lt;/span&gt;

&lt;span class="c1"&gt;// Execute in UI thread&lt;/span&gt;
&lt;span class="n"&gt;previewImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginInvoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;((()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;previewImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall implementation examples are available in &lt;a href="https://github.com/kekyo/FlashCap/tree/main/samples" rel="noopener noreferrer"&gt;Windows Forms, WPF and Avalonia&lt;/a&gt; respectively. Please refer to them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jmp0dqyln354r5jrgat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jmp0dqyln354r5jrgat.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After touching it for a while, you may be concerned about performance. Image data is very large data, and since it is video, it must be processed dozens of times per second. By default, &lt;code&gt;FlashCap&lt;/code&gt; ignores the next image data during processing. This means that "frame dropping" occurs. To solve this, the implementation of the handler must be as fast as possible.&lt;/p&gt;

&lt;p&gt;For example, image data can be stored once in a queue and processed in a multi-threaded manner. Such a scenario is also supported by the standard functionality of &lt;code&gt;FlashCap&lt;/code&gt;. Add a parameter to the &lt;code&gt;OpenAsync()&lt;/code&gt; argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Open a device with a video characteristics:&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;descriptor0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnumerateDescriptors&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ElementAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;descriptor0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;descriptor0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Characteristics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// &amp;lt;-- Enable multithreading (Scattering)&lt;/span&gt;
  &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// &amp;lt;-- Maximum queuing pixel buffers&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;bufferScope&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifies the maximum amount of image data queue, and whether it should be multithreaded or not. (This is called &lt;code&gt;Scattering&lt;/code&gt;.) It is possible to remain single-threaded and increase only the maximum queue amount. Even with single threading, if the handler processing load is unstable, frame dropping may be avoided by increasing the maximum queue size. Note that in both cases, frame dropping will occur if the image data exceeds the maximum queue size.&lt;/p&gt;

&lt;p&gt;Options are also available for those who wish to optimize processing further. If you implement your own "frame processor," you can handle almost any raw image data directly. The interface is simple but delicate and is not likely to be used in general applications. See the documentation for more details.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I have created a &lt;code&gt;FlashCap&lt;/code&gt; library that completely conceals the video capture device, yet allows programmable device coverage and format selection, has a simple and easy interface, is multi-platform, and eliminates dependencies on external libraries such as native libraries. .NET runtime version requirements as broadly as possible, and also provides interface extensibility for faster capture.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;FlashCap&lt;/code&gt; to easily obtain image data with a webcam in front of you, or to implement features that are essential for sophisticated applications, such as processing the resulting image while displaying a preview.&lt;/p&gt;

&lt;p&gt;I hope this will be helpful to everyone.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>video</category>
      <category>capture</category>
      <category>grabber</category>
    </item>
    <item>
      <title>An easy way to practice version embedding on .NET</title>
      <dc:creator>Kouji Matsui</dc:creator>
      <pubDate>Tue, 22 Mar 2022 05:00:05 +0000</pubDate>
      <link>https://forem.com/kozy_kekyo/an-easy-way-to-practice-version-embedding-on-net-45h8</link>
      <guid>https://forem.com/kozy_kekyo/an-easy-way-to-practice-version-embedding-on-net-45h8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wanted to visualize the version number of your application when you distribute it? There are many possible reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To display the version of the application when it is launched.&lt;/li&gt;
&lt;li&gt;To include it in diagnostic information for smooth support (and possibly output it to a log)&lt;/li&gt;
&lt;li&gt;Other...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From my many years of experience in software support and maintenance, I have come to believe that self-diagnostic information is important when a problem occurs. One of those materials is the version number.&lt;/p&gt;

&lt;p&gt;So, how should we manage version numbers in a .NET application? For example, consider the following console application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// No command line arguments are given.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Display help and exit&lt;/span&gt;
  &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FooBarNet 1.0.0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;-- Version number&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For very small code base, this method of hard-coding and embedding the version code is a good idea. However, in general, it is easy to make mistakes when thinking about releasing the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forgot to update the version number.&lt;/li&gt;
&lt;li&gt;You gave the wrong version number.&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem does not stop with released application binaries. Another headache is the difficulty in identifying the source code version that corresponds to the binary version number.&lt;/p&gt;

&lt;p&gt;Cooperating partners I have seen have done things like "freezing" the source code at the time of product release and compressing it, along with the compiled binaries, into a ZIP file or saving it to external storage media and labeling it.&lt;/p&gt;

&lt;p&gt;"No, no way, in this day and age?" You might think :)&lt;/p&gt;

&lt;p&gt;So how do we solve this problem in a "modern" way?&lt;/p&gt;

&lt;p&gt;For source code, it is probably common practice to use a source code management system such as Git or Subversion. Some teams may store binaries at the time of release together in these repositories.&lt;/p&gt;

&lt;p&gt;That is one way to do it, but it has the disadvantage of increasing the size of the repository. Also, there remains the problem that the first diagnostic step is not resolved when an application problem occurs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automatically embed version
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/kekyo/CenterCLR.RelaxVersioner"&gt;RelaxVersioner project&lt;/a&gt; described in this article is one way to solve this problem. Simply install this NuGet package in your project and automatically embed the version number in your application. No big deal, just install. It should be enough for you to try it out and see what happens.&lt;/p&gt;

&lt;p&gt;If you want, you can also easily reference the version number as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Refer to the version number:&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"FooBarNet &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ThisAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AssemblyVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at this code, the following questions may arise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't the version number refer to the &lt;code&gt;AssemblyVersionAttribute&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Doesn't the version number use the reflection API?&lt;/li&gt;
&lt;li&gt;How is the version number determined in the first place?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several types of version numbers in .NET, and of course, RelaxVersioner applies these standard attribute classes as well. On top of that, it also defines literal strings, as shown above, for ease of use in programmatically referencing versions.&lt;/p&gt;

&lt;p&gt;.NET standard attribute classes, so that information can be displayed even when the application binary is viewed from the Explorer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H6-cLJVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9plxkapkmn3x34cpyy01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H6-cLJVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9plxkapkmn3x34cpyy01.png" alt="Explorer image" width="363" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More detailed information can be visualized by using a decompilation tool such as ILSpy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4xCPizNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prnm0gzlujrupsp3dh46.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4xCPizNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prnm0gzlujrupsp3dh46.png" alt="ILSpy image" width="880" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will then explain the core of the last question:&lt;/p&gt;




&lt;h2&gt;
  
  
  How is the version number determined?
&lt;/h2&gt;

&lt;p&gt;If you can apply a version number simply by installing the NuGet package of RelaxVersioner, how is that version number determined?&lt;/p&gt;

&lt;p&gt;Let us reveal the seed. It refers to a tag in the Git repository. That is, RelaxVersioner refers to the local Git repository where the project is located and uses the tag with the version number applied. If there is a tag of the form "1.2.3" or "v1.2.3" separated by dots, as shown below, the commit is recognized as that version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6DQ2Df87--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odzs1dhxiu8openw5kt7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6DQ2Df87--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odzs1dhxiu8openw5kt7.png" alt="Version tagging and automatic calculation 1" width="880" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screenshots are from GitKraken.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, what happens in a commit without a tag is that the trailing value of the version number, separated by a dot, is automatically incremented. In the example above, a commit on a branch derived from 0.5.0 would be calculated to 0.5.1 or 0.5.3.&lt;/p&gt;

&lt;p&gt;Let us show a slightly less simple example. Where a merge commit occurs, the version number increment is computed on the primary parent commit first, as in the following example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EkN2Alqk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5l4ikqfki0zu6n2l5656.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EkN2Alqk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5l4ikqfki0zu6n2l5656.png" alt="Version tagging and automatic calculation 2" width="880" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, it is 0.5.1, not 0.5.6. You may think, "This would result in duplicate version numbers!", but the question is whether the application has been released or not. Once you have released your application, add a tag to it. Then the calculation will be based on the tag number.&lt;/p&gt;

&lt;p&gt;In this case, if you want the commit in the red box to be 0.5.6, add the tag "0.5.6" to this commit. There must be a reason why you want the version number to be 0.5.6 intentionally. Perhaps you want to release it, or have released it. The term "release" here is not limited to public, but private releases as well.&lt;/p&gt;

&lt;p&gt;This specification in RelaxVersioner is based on the idea that Git's local repository exists for the developer, not for release management. We wanted developers to be able to manipulate the commits of their own free (freedom) will, unless they decide to "release this commit. This way, you can enjoy the flexibility of Git while at the same time managing releases.&lt;/p&gt;




&lt;h2&gt;
  
  
  How is the version number embedded?
&lt;/h2&gt;

&lt;p&gt;Some of you may have questions about how this can be accomplished simply by installing the NuGet package of RelaxVersioner. In this section, we will briefly explain how RelaxVersioner actually embeds the version number.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, check if the directory where the .NET project exists is under a local repository in Git.&lt;/li&gt;
&lt;li&gt;then search the Git local repository for the current checkout commit.
If no commits exist at all, set the version number to 0.0.1. (either a newly created Git repository or you didn't &lt;code&gt;git init&lt;/code&gt; it in the first place)&lt;/li&gt;
&lt;li&gt;if the commit exists and a tag corresponding to the version number is found, use that version number.
If the commit exists but does not have a tag, the tag corresponding to the version number is found by traversing the parent commits in order.&lt;/li&gt;
&lt;li&gt;based on the found version number and the MSBuild property information group, the template source code is generated based on the rule file.
The default rule file is built in, so if it does not exist, use the default.
The template source code generates C#, F#, VB.NET, and C++/CLI code in a temporary directory according to the language used in the project.&lt;/li&gt;
&lt;li&gt;ensure that the template source code is included in the MSBuild build target.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps are performed by MSBuild scripts and the RelaxVersioner command line tool. When the tool runs at build time, you will see a message similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1&amp;gt;RelaxVersioner[2.5.5]: Generated versions code: Language=C#, Version=0.7.12
1&amp;gt;FlashCap -&amp;gt; C:\Projects\FlashCap\FlashCap\bin\Debug\net6.0\FlashCap.dll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your project is multi-platform, you will see many of the same messages in the build log, which is not unusual, because RelaxVersioner generates template source code for each platform.&lt;/p&gt;

&lt;p&gt;And the template source code is as follows (in C#) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Reflection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0.21"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2020.12.20.33529"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyInformationalVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Sun, 20 Dec 2020 09:37:39 GMT"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Branch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"master"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tags"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Author"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Kouji Matsui &amp;lt;k@kekyo.net&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Committer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Kouji Matsui &amp;lt;k@kekyo.net&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Merge branch 'devel'"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Sun, 20 Dec 2020 09:37:43 GMT"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Platform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"AnyCPU"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BuildOn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Unix"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SdkVersion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"5.0.101"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;YourApp&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThisAssembly&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AssemblyVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.21"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AssemblyFileVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2020.12.20.33529"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AssemblyInformationalVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssemblyMetadata&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Sun, 20 Dec 2020 09:37:39 GMT"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Branch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"master"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Kouji Matsui &amp;lt;k@kekyo.net&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Committer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Kouji Matsui &amp;lt;k@kekyo.net&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Merge branch 'devel'"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Generated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Sun, 20 Dec 2020 09:37:43 GMT"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AnyCPU"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;BuildOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Unix"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;SdkVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"5.0.101"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, each attribute applies and defines members that can be referenced as literal strings.&lt;/p&gt;

&lt;p&gt;This source code is placed in a temporary directory. Generally, it is in "obj/" in the project directory. Thus, this source code will not cause unwanted contamination of the commits in the Git repository. And because the entire process is fully automated, it can also be used for continuous integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Packaging
&lt;/h2&gt;

&lt;p&gt;Did you know that you can easily NuGet package a library project using the dotnet CLI commands? If you have the library project at hand, you can generate it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet pack &lt;span class="nt"&gt;-p&lt;/span&gt;:Configuration&lt;span class="o"&gt;=&lt;/span&gt;Release &lt;span class="nt"&gt;-o&lt;/span&gt; artifacts FooBarComponent&lt;span class="se"&gt;\F&lt;/span&gt;ooBarComponent.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a package file in the &lt;code&gt;artifacts&lt;/code&gt; directory, such as &lt;code&gt;FooBarComponent.1.0.0.nupkg&lt;/code&gt;. Upload it to &lt;a href="https://nuget.org/"&gt;nuget.org&lt;/a&gt; and you are done. It's that easy.&lt;/p&gt;

&lt;p&gt;... Easy, isn't it?&lt;/p&gt;

&lt;p&gt;Uploading and publishing a package requires various metadata:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The version number of the package&lt;/li&gt;
&lt;li&gt;Project site URL&lt;/li&gt;
&lt;li&gt;Name of the authors, etc.&lt;/li&gt;
&lt;li&gt;Source code repository URL&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JGIZ0L6_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eteuc6kyy9n6esqbvr1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JGIZ0L6_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eteuc6kyy9n6esqbvr1q.png" alt="NuGet package example" width="850" height="923"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Among these, there are several pieces of information that should be versioned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package version&lt;/li&gt;
&lt;li&gt;Version information applicable to the binaries (assembly files) contained in the package&lt;/li&gt;
&lt;li&gt;Source code repository URL&lt;/li&gt;
&lt;li&gt;The corresponding commit ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository URL and commit ID are optional, but if this metadata can be provided, a powerful debugger feature called &lt;a href="https://github.com/dotnet/sourcelink"&gt;SourceLink&lt;/a&gt; becomes available.&lt;/p&gt;

&lt;p&gt;For example, the famous &lt;a href="https://www.nuget.org/packages/Newtonsoft.Json/13.0.2-beta1"&gt;Newtonsoft.Json&lt;/a&gt; package, which we have all used, allows you to step into methods in that package and reference implementations.&lt;/p&gt;

&lt;p&gt;You can step into methods contained in the package and reference the implementation. IDEs such as Visual Studio can then automatically download source code from GitHub and continue debugging on the fly, as if the entire project existed locally.&lt;/p&gt;

&lt;p&gt;To add even more value, you can use RelaxVersioner to apply versioning to NuGet packages completely automatically: install the RelaxVersioner package in your project, and run same as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet pack &lt;span class="nt"&gt;-p&lt;/span&gt;:Configuration&lt;span class="o"&gt;=&lt;/span&gt;Release &lt;span class="nt"&gt;-o&lt;/span&gt; artifacts FooBarComponent&lt;span class="se"&gt;\F&lt;/span&gt;ooBarComponent.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to support SourceLink, &lt;a href="https://github.com/kekyo/CenterCLR.RelaxVersioner#sourcelink-integration"&gt;you will need to add an additional definition&lt;/a&gt; to &lt;code&gt;FooBarComponent.csproj&lt;/code&gt;, but only once. In other words, once you install RelaxVersioner, you can consistently version control everything from assembly file generation to NuGet package generation using only Git.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;.NET applications, "RelaxVersioner" can efficiently manage version numbers using tagging in Git repositories.&lt;/p&gt;

&lt;p&gt;This reduces the time and effort required to manage version numbers by hard-coding source code, and also allows you to manage the version of the source code itself. The applied version number can be easily referenced from the code and included in the built binary, allowing the user to reference it in the explorer.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>versioning</category>
      <category>git</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
