・2022/05/05
Accord.NETフレームワークを使って USBカメラから動画を録画してファイルに保存する方法
(C#で USBカメラや MS2109の HDMIキャプチャデバイスから画像をキャプチャして録画する方法)
Tags: [Windows], [無人インストール]
● Accord.NETフレームワークを使って USBカメラから動画を録画してファイルに保存する方法
C#で USBカメラや MS2109の HDMIキャプチャデバイスから画像をキャプチャして録画する方法
なお、結論から書くと無音の動画のキャプチャ(ファイル保存)はできますが、音声付きの動画のキャプチャは成功しませんでした。
Accord.NETフレームワーク
Accord.NET Machine Learning Framework
Accord.NET Framework
Accord.NETフレームワークは 2017年で開発が停止しています。
Accord.NETフレームワークは 3.8.0を使用しています。
Accord 3.8.0
3.8.2-alphaを使用しても音声付きの動画ができませんでした。(音声にブチブチノイズが入る。動画のフレームレートが変)
とりあえず、これで MS2109の動画キャプチャで Windwos標準のカメラアプリや OBS等の巨大サイズのアプリを使わなくても済む様になりました。
(音声を録音したい場合は OBSを使うしか方法がありませんが)
なお、PictureBoxに PictureBoxSizeMode.StretchImageを使うと私の使い方が間違っているのか? pictureBox.Image.Dispose() をしても盛大にメモリリークしまくります。
なので、PictureBoxには自前で拡大縮小の処理を入れています。
SCALE AN IMAGE IN C# PRESERVING ASPECT RATIO
Resize code ruins quality? [duplicate]
と思い、再度追試したら大丈夫でした。私の PictureBoxの使い方が間違っていた様です。
PictureBoxSizeMode.Zoomを設定すれば画像の縦横比(Aspect ratio)を保持して自動リサイズをしてくれます。
Accord.3.8.0
Accord.Audio.3.8.0
Accord.DirectSound.3.8.0
Accord.Math.3.8.0
Accord.Video.3.8.0
Accord.Video.DirectShow.3.8.0
Accord.Video.FFMPEG.3.8.0
SharpDX.4.2.0
SharpDX.DirectSound.4.2.0
※ Audio系は未使用です。
Windowsフォームアプリケーションでプロジェクトを作成します。
下記の画像の様に ComboBoxを 3個、Buttonを 2個、CheckBoxを 1個、PictureBoxを 2個、Formに配置します。
右下の PictureBoxは Capture時のサムネイル表示用。
・Accord.NETフレームワークを使って USBカメラから動画を録画してファイルに保存する方法


using Accord.Audio;
using Accord.Audio.Generators;
using Accord.DirectSound;
using Accord.Video.DirectShow;
using Accord.Video.FFMPEG;
using System;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestAccordApp
{
public partial class Form1 : Form
{
private FilterInfoCollection videoDevices;
private AudioDeviceCollection audioDevices;
private VideoCaptureDevice videoCapture;
private VideoFileWriter videoFileWrite;
private System.Timers.Timer timer1;
private Object syncObj = new Object();
volatile bool capture = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
button1.Enabled = false;
button2.Enabled = false;
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox2.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox2.Enabled = false;
comboBox4.DropDownStyle = ComboBoxStyle.DropDownList;
// pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
// pictureBox2.SizeMode = PictureBoxSizeMode.CenterImage;
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
pictureBox2.SizeMode = PictureBoxSizeMode.Zoom;
pictureBox2.Visible = false;
timer1 = new System.Timers.Timer(800);
timer1.Interval = 800;
timer1.Elapsed += (send, ev) =>
{
Invoke(new Action(TimerEvent));
};
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (var vd in videoDevices)
{
comboBox1.Items.Add(vd.Name);
}
comboBox1.SelectedIndex = 0;
// audioDevices = new AudioDeviceCollection(AudioDeviceCategory.Capture);
// foreach (var ad in audioDevices)
// {
// comboBox3.Items.Add(ad.Description);
// }
comboBox4.Items.Add("V. High");
comboBox4.Items.Add("High");
comboBox4.Items.Add("Normal");
comboBox4.Items.Add("Low");
comboBox4.Items.Add("V. Low");
comboBox4.SelectedIndex = 2;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Invoke((MethodInvoker)delegate
{
StopCamera();
timer1.Dispose();
audioDevices = null;
videoDevices = null;
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
if (pictureBox2.Image != null)
{
pictureBox2.Image.Dispose();
}
});
}
private void StartCamera()
{
var index = comboBox1.SelectedIndex;
if (index < 0)
{
return;
}
var index2 = comboBox2.SelectedIndex;
if (index2 < 0)
{
return;
}
try
{
videoCapture = new VideoCaptureDevice(videoDevices[index].MonikerString);
videoCapture.VideoResolution = videoCapture.VideoCapabilities[index2];
videoCapture.NewFrame += Camera_NewFrame;
videoCapture.Start();
}
catch (Exception _)
{
}
var vr = videoCapture.VideoResolution;
int width = vr.FrameSize.Width;
int height = vr.FrameSize.Height;
int frameRate = vr.AverageFrameRate;
VideoCodec codec = VideoCodec.MPEG4;
var qty = comboBox4.SelectedIndex - 2;
int bitRate = (int)(width * height / (1.0 + 0.25 * qty));
DateTime dt = DateTime.Now;
string str = dt.ToString("yyyyMMdd_HHmmss");
if (checkBox1.Checked)
{
lock (syncObj)
{
videoFileWrite = new VideoFileWriter();
videoFileWrite.Open(str + ".mp4", width, height, frameRate, codec, bitRate);
}
}
}
private void StopCamera()
{
lock (syncObj)
{
if (videoFileWrite != null)
{
videoFileWrite.Close();
videoFileWrite.Dispose();
videoFileWrite = null;
}
}
if (videoCapture != null)
{
videoCapture.NewFrame -= Camera_NewFrame;
videoCapture.SignalToStop();
Task task = Task.Run(() =>
{
videoCapture.WaitForStop();
videoCapture = null;
});
}
}
private void Camera_NewFrame(object sender, Accord.Video.NewFrameEventArgs eventArgs)
{
var bitmap = (Bitmap)eventArgs.Frame;
// InvokeRequired
Invoke(new Action<Bitmap>(UpdateFrame), bitmap);
lock (syncObj)
{
if (videoFileWrite != null)
{
videoFileWrite.WriteVideoFrame(bitmap);
}
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
private void UpdateFrame(Bitmap bitmap)
{
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
var maxWidth = pictureBox1.Width;
var maxHeight = pictureBox1.Height;
// pictureBox1.Image = ScaleImage(bitmap, maxWidth, maxHeight);
pictureBox1.Image = (Bitmap)bitmap.Clone();
if (capture)
{
DateTime dt = DateTime.Now;
string str = dt.ToString("yyyyMMdd_HHmmss");
string filename = @".\" + str + ".png";
var imageFormat = System.Drawing.Imaging.ImageFormat.Png;
bitmap.Save(filename, imageFormat);
if (pictureBox2.Image != null)
{
pictureBox2.Image.Dispose();
}
var maxWidth2 = pictureBox2.Width;
var maxHeight2 = pictureBox2.Height;
// pictureBox2.Image = ScaleImage(bitmap, maxWidth2, maxHeight2);
pictureBox2.Image = (Bitmap)bitmap.Clone();
pictureBox2.Visible = true;
timer1.Stop();
timer1.Start();
capture = false;
}
}
// SCALE AN IMAGE IN C# PRESERVING ASPECT RATIO
// https://efundies.com/scale-an-image-in-c-sharp-preserving-aspect-ratio/
private Bitmap ScaleImage(Bitmap bmp, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / bmp.Width;
var ratioY = (double)maxHeight / bmp.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(bmp.Width * ratio);
var newHeight = (int)(bmp.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage))
graphics.DrawImage(bmp, 0, 0, newWidth, newHeight);
return newImage;
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var cb = (ComboBox)sender;
var index = cb.SelectedIndex;
button1.Enabled = false;
if (index < 0)
{
}
else
{
comboBox2.Items.Clear();
var vcd = new VideoCaptureDevice(videoDevices[index].MonikerString);
foreach (VideoCapabilities vc in vcd.VideoCapabilities)
{
var item = vc.FrameSize.Width + ", " + vc.FrameSize.Height + " " + vc.AverageFrameRate + "fps";
comboBox2.Items.Add(item);
}
vcd = null;
comboBox2.Enabled = true;
comboBox2.SelectedIndex = 0;
}
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
var cb = (ComboBox)sender;
var index = cb.SelectedIndex;
if (index < 0)
{
button1.Enabled = false;
}
else
{
button1.Enabled = true;
}
}
private void button1_Click(object sender, EventArgs e)
{
if (videoCapture != null && videoCapture.IsRunning)
{
StopCamera();
button1.Text = "Start";
button2.Enabled = false;
comboBox1.Enabled = true;
comboBox2.Enabled = true;
}
else
{
StartCamera();
button1.Text = "Stop";
button2.Enabled = true;
comboBox1.Enabled = false;
comboBox2.Enabled = false;
}
}
private void button2_Click(object sender, EventArgs e)
{
capture = true;
}
private void TimerEvent()
{
timer1.Stop();
pictureBox2.Visible = false;
}
}
}
●音声付きの動画のキャプチャは動画と音声を別々にファイルに書き出して FFmpeg.exeで合体する方法で解決できました
これで OBS Studioを捨てる事ができる!
using Accord.Audio;
using Accord.Audio.Generators;
using Accord.DirectSound;
private AudioDeviceCollection audioDevices;
private AudioCaptureDevice audioDevice;
private BinaryWriter audioFileWrite;
{
audioDevices = new AudioDeviceCollection(AudioDeviceCategory.Capture);
foreach (var ad in audioDevices)
{
comboBox3.Items.Add(ad.Description);
}
}
private void StartCamera()
{
// MS2109の 96kHz モノラルでキャプチャする
audioDevice = new AudioCaptureDevice(audioDevices.ElementAt(index3));
audioDevice.Format = SampleFormat.Format32BitIeeeFloat;
audioDevice.SampleRate = 96000; // 96.0kHz
audioDevice.DesiredFrameSize = 4096;
audioDevice.Channels = 1;
audioDevice.NewFrame += Audio_NewFrame;
audioDevice.Start();
lock (syncObj)
{
audioFileWrite = new BinaryWriter(new FileStream("xxxx.raw", FileMode.CreateNew));
}
}
void Audio_NewFrame(object sender, Accord.Audio.NewFrameEventArgs eventArgs)
{
lock (syncObj)
{
if (audioFileWrite != null)
{
var s = eventArgs.Signal;
audioFileWrite.Write(s.RawData, 0, s.RawData.Length);
}
}
}
private void StopCamera()
{
lock (syncObj)
{
if (audioFileWrite != null)
{
audioFileWrite.Close();
audioFileWrite = null;
}
}
if (audioDevice != null)
{
audioDevice.NewFrame -= Audio_NewFrame;
audioDevice.SignalToStop();
Task task = Task.Run(() =>
{
audioDevice.WaitForStop();
audioDevice = null;
});
}
}
comboBox5.Items.Add("44.1Khz Stereo");
comboBox5.Items.Add("48Khz Stereo");
comboBox5.Items.Add("44.1Khz Mono");
comboBox5.Items.Add("48Khz Mono");
comboBox5.Items.Add("96Khz Mono(MS2109)");
...
audioDevice = new AudioCaptureDevice(audioDevices.ElementAt(index3));
audioDevice.Format = SampleFormat.Format16Bit;
// audioDevice.Format = SampleFormat.Format32BitIeeeFloat;
var index5 = comboBox5.SelectedIndex;
switch (index5) {
case 0:
audioDevice.SampleRate = 44100;
audioDevice.Channels = 2;
break;
case 1:
audioDevice.SampleRate = 48000;
audioDevice.Channels = 2;
break;
case 2:
audioDevice.SampleRate = 44100;
audioDevice.Channels = 1;
break;
case 3:
audioDevice.SampleRate = 48000;
audioDevice.Channels = 1;
break;
case 4:
audioDevice.SampleRate = 96000; // 48kHz
audioDevice.Channels = 1; // Stereo
break;
}
audioDevice.DesiredFrameSize = 4096;
audioDevice.NewFrame += Audio_NewFrame;
audioDevice.Start();
● ffmpegを使って動画と音声を合体する方法
rem 動画と音声を合体させる
ffmpeg -i xxxx.mp4 -i xxxx.wav -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 new_xxxx.mp4
● USB HDMIキャプチャの MS2109の 96kHz モノラルを 48kHz ステレオの wav形式に変換する
MS2109の音声キャプチャのバグを修正します。
・96kHz モノラルを 48kHz ステレオにする
・ステレオの左右の左側右側を正しくする
rem MS2109の 96kHz モノラルを 48kHz ステレオの wav形式に変換する
ffmpeg -f f32le -ar 48000 -ac 2 -i xxxx.raw -map_channel 0.0.1 -map_channel 0.0.0 -ar 48000 -ac 2 xxxx.wav
または、
ffmpeg -f f32le -ar 48000 -ac 2 -i xxxx.raw -af pan=stereo|c0=c1|c1=c0 -ar 48000 -ac 2 xxxx.wav
rem 動画と音声を合体させる
ffmpeg -i xxxx.mp4 -i xxxx.wav -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 new_xxxx.mp4
●音声付きの動画のキャプチャは成功しませんでした 3.8.0
WriteAudioFrameで AccessViolationExceptionや IndexOutOfRangeExceptionの例外が発生する。
上記の別々キャプチャで動画と音声を合体する方法で回避する。
How to save Video (from webcam) and Audio (from directsound device) in avi file ? #1040
VideoFileWriter WriteAudioFrame AccessViolationException #1428
Please help if somebody solved this puzzle.
Save both audio and video together not working #1834
void Audio_NewFrame(object sender, Accord.Audio.NewFrameEventArgs eventArgs)
{
lock (syncObj)
{
if (videoFileWrite != null)
{
var s = eventArgs.Signal;
// AccessViolationException
videoFileWrite.WriteAudioFrame(s.RawData);
// AccessViolationException
videoFileWrite.WriteAudioFrame((byte[])s.RawData.Clone());
// IndexOutOfRangeException
videoFileWrite.WriteAudioFrame(s.RawData);
}
}
}
●音声付きの動画のキャプチャは成功しませんでした 3.8.2-alpha
3.8.2-alphaを使用しても音声付きの動画ができませんでした。(音声にブチブチノイズが入る。動画のフレームレートが変)
VideoFileWriter.WriteAudioFrame - issue with stereo signal (3.8.2 prerelese) #1353
VideoFileWriter: why is my audio half the speed of my video? #600
結論:I abandoned this framework.
意訳:俺は Accord.NETフレームワークを捨てた
● AForge.NETフレームワーク
Accordと類似の AForgeが有りますが、AForgeの方が古いです。
AForge.NETフレームワークは 2013年で開発が停止しています。
AForge.NET - Computer Vision, Artificial Intelligence, Robotics
AForge.NET Framework
AForge.NET Framework
AForge.NETフレームワークは Audio系、FFmpeg系のモジュールが有りません。
Adding audio to a video?
There is no audio support in AForge.NET Framework.
Video Created has no Audio
No. AForge.NET Framework never had anything to do with sound - not supported.
video file but no sounds
The aim of the AForge.NET framework was to give tools to do image processing, computer vision tasks, some video related stuff and other things. But it was not aimed for building video players or for sound processing, so there is zero support of sound.
意訳:AForge.NETは「画像処理」が目的なのでオーディオ系のサポートは無いよ。
DirectShow .NET
DirectShow .NETは 2010年で開発停止。
2022年現在も開発が続いている C#用の画像処理系のパッケージは OpenCvSharp位かな?
shimat / opencvsharp
4.5.3.20211228
29 Dec 2021
手軽に動画と音声を動画ファイルとして保存できるパッケージは無いのかな?
Nugetを FFmpegで検索すると 330個も出てくるので、どれが目的のパッケージがわからん。
330 packages returned for FFmpeg
今の所、音声は NAudio等のパッケージを使用して、動画ファイルと音声ファイルを別々に生成してから、別途合体させる方法しか思いつかない。
※ 上記の通り NAudioを使わなくても AudioCaptureDeviceで音声キャプチャで解決しました。
NAudio is an open source .NET audio library written by Mark Heath
Tags: [Windows], [無人インストール]
●関連するコンテンツ(この記事を読んだ人は、次の記事も読んでいます)
USB HDMIキャプチャの MS2109でパソコンを Nintendo Switch等のゲーム機のディスプレイに変える
MS2109の映像を超低遅延で表示する方法、市販の Sh@d0wC@stや 0miP1@yと同じに使える
最近流行の 1000円で買える USBで HDMI映像信号をキャプチャする MS2109ドングルを買ってみた
接続してすぐに使える USB接続式 HDMI信号キャプチャ(映像+音声) MS2109使用
最近流行の 1000円で買える USBで HDMI映像信号をキャプチャする MS2109ドングルを買ってみた
接続してすぐに使える USB接続式 HDMI信号キャプチャ(映像+音声) MS2109使用
MS2109で有名なバグ、ステレオの音声がモノラルになるのを修正するアプリ mono-to-stereo
MS2109の音声バグを修正する mono-to-stereoをデフォルトで日本語 Windows対応に改造してみた
.NET C#の Tweetinviライブラリで Twitterの投稿内容を投稿画像込みで丸っと取得する方法
まるっと取得したかった!質問は受け付けない!
EKSA ゲーミングヘッドセットのレビュー E900Pro、2年間の品質保証
軽量 素直な高音質 switch/PC/パソコン/Xbox One/スマホ対応、3.5mm接続、USB接続の両方に対応
[HOME]
|
[BACK]
リンクフリー(連絡不要、ただしトップページ以外は Web構成の変更で移動する場合があります)
Copyright (c)
2022 FREE WING,Y.Sakamoto
Powered by 猫屋敷工房 & HTML Generator
http://www.neko.ne.jp/~freewing/software/visual_c_sharp_accord_video_usb_camera_capture/