using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Diagnostics;
using TIS.Imaging;
using System.Windows.Forms;
namespace ISCamRecorder {
internal class ISCamera {
readonly string ICCF_FILE = @"dfk33ux290.iccf"; // 設定ファイル
readonly string CAMERA_FORMAT = "RGB32 (1920x1080)"; // 設定ファイル無い時の解像度
readonly float FRAME_RATE = 40F; // 設定ファイル無い時のFPS
readonly int JPEG_QUALITY = 90; // JPEG保存品質
MainForm _MF = null; // メインフォームインスタンス
ICImagingControl _Cam; // カメラオブジェクト
string _SerialNumber; // シリアル番号
string _CamID; // カメラID
VCDButtonProperty _Trigger; // ソフトウェアトリガー発信オブジェクト
FrameRateCounter _Fps = new FrameRateCounter(10); // フレームレート計測
FrameQueueSink _PreviewSink; // プレビューSink
FrameQueueSink _RecSink; // 録画Sink
SinkListener _SinkListener = new SinkListener(); // Sinkリスナー
IFrameQueueBuffer[] _bufferlist = null; // 録画バッファ
bool _Recoding = false; // 録画中フラグ
public ManualResetEvent Ready { get; private set; } // 録画準備完了シグナル
public float CameraFPS { get; private set; } = 0; // カメラFPS
public bool DeviceValid { get { return _Cam?.DeviceValid ?? false; } } // デバイス有効・無効
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="cameraControl"></param>
/// <param name="serialNumber"></param>
public ISCamera(MainForm mf, ICImagingControl cameraControl, string serialNumber) {
_MF = mf;
_Cam = cameraControl;
_SerialNumber = serialNumber;
_CamID = $"Cam{_SerialNumber.Substring(_SerialNumber.Length - 2)}";
_RecSink = new FrameQueueSink(_SinkListener, MediaSubtypes.RGB32);
_PreviewSink = new FrameQueueSink(CaptureFrame, MediaSubtypes.RGB32, 5);
Ready = new ManualResetEvent(false);
}
/// <summary>
/// カメラ接続とプレビュー開始
/// </summary>
/// <returns></returns>
public bool Connect() {
if (_Cam.DeviceValid) return true;
// 接続
var dev = _Cam.Devices.FirstOrDefault(
c => c.GetSerialNumber().Equals(_SerialNumber));
if (dev == null) return false;
_Cam.Device = dev;
if (!_Cam.DeviceValid) return false;
// 撮影条件設定
try {
if (File.Exists(ICCF_FILE)){
_Cam.LoadDeviceState(ICCFImport.Import(ICCF_FILE), false);
Common.DebugOut($"{_CamID} loaded config {ICCF_FILE}");
} else {
//Common.DebugOut($"Can't find config {ICCF_FILE}");
_Cam.VideoFormat = _Cam.VideoFormats.FirstOrDefault(
c => c.Name.Equals(CAMERA_FORMAT));
_Cam.DeviceFrameRate = _Cam.DeviceFrameRates.FirstOrDefault(
c => (c > FRAME_RATE - 0.1F && c < FRAME_RATE + 0.1F));
Common.DebugOut($"{_CamID} sets {_Cam.VideoFormat}, {_Cam.DeviceFrameRate:0.0}fps");
}
} catch (ICException iex) {
MessageBox.Show(iex.Message, $"{_CamID} Error",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
CameraFPS = _Cam.DeviceFrameRate;
_Cam.DeviceTrigger = false; // 起動時はトリガーOFF
_Trigger = _Cam.VCDPropertyItems.Find<VCDButtonProperty>(
VCDGUIDs.VCDID_TriggerMode, VCDGUIDs.VCDElement_SoftwareTrigger);
// 表示設定
_Cam.LiveDisplayDefault = false;
SetDisplaySize();
_Cam.Sink = _PreviewSink;
_Cam.LiveStart();
return true;
}
/// <summary>
/// Sink変更
/// </summary>
/// <param name="isRec">true:録画 false:プレビュー</param>
public void ChangeSink(bool isRec) {
if (!_Cam.DeviceValid) return;
_Cam.LiveStop();
if (isRec) _Cam.Sink = _RecSink;
else _Cam.Sink = _PreviewSink;
_Cam.LiveStart();
}
/// <summary>
/// 画像撮影
/// </summary>
public void SnapImage() {
if (!_Cam.DeviceValid) return;
TIS.Imaging.FrameSnapSink snapSink = new TIS.Imaging.FrameSnapSink();
_Cam.LiveStop();
_Cam.Sink = snapSink;
_Cam.LiveStart();
TIS.Imaging.IFrameQueueBuffer frm = snapSink.SnapSingle(TimeSpan.FromSeconds(5));
var outDir = Path.Combine(_MF.Setting.SaveDir, "image");
var filename = Path.Combine(outDir, $"{_CamID}_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.{_MF.Setting.ImageType}");
switch (_MF.Setting.ImageType) {
case "jpg":
frm.SaveAsJpeg(filename, JPEG_QUALITY);
break;
case "bmp":
frm.SaveAsBitmap(filename);
break;
case "tif":
frm.SaveAsTiff(filename);
break;
}
_Cam.LiveStop();
_Cam.Sink = _PreviewSink;
_Cam.LiveStart();
}
/// <summary>
/// 録画
/// </summary>
public void RecordToMemory(float recodingLimit) {
if (!_Cam.DeviceValid) return;
_Recoding = false;
// メモリ確保
ClearBuffer();
var framesToCapture = (int)(recodingLimit * CameraFPS) + 1;
_RecSink.AllocAndQueueBuffers(framesToCapture);
Ready.Set();
Common.DebugOut($"{_CamID} ready for recoding.");
// 撮影待機
while (!_Recoding) { Thread.Sleep(0); }
// 撮影
Common.DebugOut($"{_CamID} starts recoding.");
while (_Recoding) {
if (framesToCapture <= _RecSink.OutputQueueSize + 1) break;
}
// 画像バッファに変換
_bufferlist = _RecSink.PopAllOutputQueueBuffers();
_Recoding = false;
Common.DebugOut($"{_CamID} ends recoding with {_bufferlist.Length} frames.");
}
// 録画開始
public void StartRecoding() {
_Recoding = true;
}
/// <summary>
/// ファイル保存
/// </summary>
/// <param name="outDir"></param>
public void SaveToFile() {
if (!_Cam.DeviceValid) return;
Common.DebugOut($"{_CamID} starts saving with {CameraFPS}fps.");
// 保存先確保
var outDir2 = Path.Combine(_MF.OutputDir, _CamID);
Directory.CreateDirectory(outDir2);
// 動画保存準備
var movieFile = Path.Combine(_MF.OutputDir, $"{_CamID}_{_MF.RecodingTimeStr}.mp4");
var writer = new H264Writer(movieFile,
_RecSink.OutputFrameType, (int)CameraFPS, _MF.Setting.MovieRate * 1000);
writer.Begin();
// ファイル保存
var firstDriverTime = _bufferlist[0].FrameMetadata.DriverFrameFirstPacketTime;
for (int i = 0; i < _bufferlist.Length; i++) {
writer.Write(_bufferlist[i]);
var driverTime = _bufferlist[i].FrameMetadata.DriverFrameFirstPacketTime;
var frameTime = _MF.RecodingTime.Add(driverTime - firstDriverTime); // パケットをドライバが受信した時刻
string strSampleTime = frameTime.ToString(@"HHmmss\.fff");
var fileName = $"{_CamID}_{strSampleTime}.{_MF.Setting.FrameType}";
var filePath = Path.Combine(outDir2, fileName);
try {
switch (_MF.Setting.FrameType) {
case "jpg":
FrameExtensions.SaveAsJpeg(_bufferlist[i], filePath, JPEG_QUALITY);
break;
case "bmp":
FrameExtensions.SaveAsBitmap(_bufferlist[i], filePath);
break;
case "tif":
FrameExtensions.SaveAsTiff(_bufferlist[i], filePath);
break;
}
} catch {
}
}
// 終了処理
writer.End();
ClearBuffer();
Common.DebugOut($"{_CamID} ends saving.");
}
/// <summary>
/// 連続フレーム記録バッファの解放
/// </summary>
private void ClearBuffer() {
if (_bufferlist != null) {
_bufferlist = null;
GC.Collect();
}
}
/// <summary>
/// 録画中断
/// </summary>
public void StopRecoding() {
_Recoding = false;
}
/// <summary>
/// 表示サイズ設定
/// </summary>
public void SetDisplaySize() {
if (!_Cam.DeviceValid) return;
var vf = _Cam.VideoFormatCurrent;
if (vf.Height * _Cam.Width < vf.Width * _Cam.Height) {
_Cam.LiveDisplayLeft = 0;
_Cam.LiveDisplayWidth = _Cam.Width;
var h = vf.Height * _Cam.Width / vf.Width;
_Cam.LiveDisplayTop = (_Cam.Height - h) / 2;
_Cam.LiveDisplayHeight = h;
} else {
_Cam.LiveDisplayTop = 0;
_Cam.LiveDisplayHeight = _Cam.Height;
var w = vf.Width * _Cam.Height / vf.Height;
_Cam.LiveDisplayLeft = (_Cam.Width - w) / 2;
_Cam.LiveDisplayWidth = w;
}
}
/// <summary>
/// フレーム取得時
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
private FrameQueuedResult CaptureFrame(IFrameQueueBuffer buffer) {
_Fps.Shot();
return FrameQueuedResult.ReQueue;
}
/// <summary>
/// トリガーモード変更
/// </summary>
/// <param name="enable"></param>
public void SetTriggerMode(bool enable) {
if (!_Cam.DeviceValid) return;
_Cam.LiveStop();
_Cam.DeviceTrigger = enable;
_Cam.LiveStart();
}
/// <summary>
/// ソフトウェアトリガー発信
/// </summary>
public void SWTrigger() {
if (_Cam.DeviceValid && _Trigger != null) _Trigger.Push();
}
/// <summary>
/// プロパティ設定(ダイアログ)
/// </summary>
/// <returns></returns>
public string SetProperty() {
if (!_Cam.DeviceValid) return "";
_Cam.ShowPropertyDialog();
return _Cam.SaveDeviceState();
}
/// <summary>
/// プロパティ設定(条件指定)
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
public string SetProperty(string config) {
if (!_Cam.DeviceValid) return "";
_Cam.LiveStop();
_Cam.LoadDeviceState(config, false);
_Cam.LiveStart();
return config;
}
/// <summary>
/// カメラ情報文字列
/// </summary>
/// <returns></returns>
public string CameraInfo() {
return $"({_SerialNumber}) {_Fps.FrameRate:0.0} fps";
}
/// <summary>
/// 1秒録画に要するメモリ量
/// </summary>
/// <returns></returns>
public float ConsumedMemoryPerSecond() {
if (!_Cam.DeviceValid) return 0;
var vf = _Cam.VideoFormatCurrent;
int frameSize = vf.BitsPerPixel / 8 * vf.Width * vf.Height;
int bcs = 1024 * 1024;
return _Fps.FrameRate * frameSize / bcs;
}
}
}