554 lines
12 KiB
C#
554 lines
12 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using FFmpeg.AutoGen;
|
|
using VideoReader.Properties;
|
|
|
|
namespace VideoReader;
|
|
|
|
internal class Decoder
|
|
{
|
|
public struct Fram
|
|
{
|
|
public long num;
|
|
|
|
public long key;
|
|
|
|
public byte[] data;
|
|
|
|
public Fram(long Num, long Key, byte[] Data)
|
|
{
|
|
num = Num;
|
|
key = Key;
|
|
data = Data;
|
|
}
|
|
}
|
|
|
|
private bool save_video_flag;
|
|
|
|
public bool save_video;
|
|
|
|
private DateTime endFrame = DateTime.Now;
|
|
|
|
private unsafe AVCodec* codec;
|
|
|
|
private unsafe sbyte* pConvertedFrameBuffer = null;
|
|
|
|
private unsafe sbyte* pConvertedFrameBufferEnd = null;
|
|
|
|
public long endkey;
|
|
|
|
private DateTime timeForFPS = DateTime.MinValue;
|
|
|
|
private int frameForFPS;
|
|
|
|
public double FPS;
|
|
|
|
private bool dec;
|
|
|
|
private Queue<double> fpsAver;
|
|
|
|
public ConcurrentDictionary<long, Fram> frames;
|
|
|
|
private Thread saveVideo;
|
|
|
|
private bool startSaveVideo;
|
|
|
|
private bool blockSaveVideo;
|
|
|
|
private DateTime endAddSaveVideo = DateTime.MinValue;
|
|
|
|
private ConcurrentDictionary<long, byte[]> save = new ConcurrentDictionary<long, byte[]>();
|
|
|
|
private int fps = 30;
|
|
|
|
private byte[] configN;
|
|
|
|
private ConcurrentQueue<string> outstr = new ConcurrentQueue<string>();
|
|
|
|
private ConcurrentQueue<string> outstr1 = new ConcurrentQueue<string>();
|
|
|
|
private ConcurrentQueue<byte[]> save_buff = new ConcurrentQueue<byte[]>();
|
|
|
|
private object block = new object();
|
|
|
|
private string name = DateTime.Now.Ticks + ".tmp";
|
|
|
|
private string nam0 = DateTime.Now.Ticks + ".tm0";
|
|
|
|
private long GetImgEndFrame;
|
|
|
|
private long GetImgEndKey = long.MaxValue;
|
|
|
|
public long min
|
|
{
|
|
get
|
|
{
|
|
if (frames.Count > 0)
|
|
{
|
|
return frames.Keys.Min();
|
|
}
|
|
return 0L;
|
|
}
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public long max
|
|
{
|
|
get
|
|
{
|
|
if (frames.Count > 0)
|
|
{
|
|
return frames.Keys.Max();
|
|
}
|
|
return 0L;
|
|
}
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public long count
|
|
{
|
|
get
|
|
{
|
|
if (frames.Count > 0)
|
|
{
|
|
return frames.Count;
|
|
}
|
|
return 0L;
|
|
}
|
|
set
|
|
{
|
|
}
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
frameForFPS = 0;
|
|
fpsAver = new Queue<double>();
|
|
frames = new ConcurrentDictionary<long, Fram>();
|
|
endkey = -1L;
|
|
timeForFPS = DateTime.MinValue;
|
|
FPS = 0.0;
|
|
dec = false;
|
|
}
|
|
|
|
private void SaveVideo()
|
|
{
|
|
while (startSaveVideo)
|
|
{
|
|
int num = fps * 60;
|
|
if (save.Count > num || (DateTime.Now - endAddSaveVideo).TotalSeconds > 10.0)
|
|
{
|
|
Queue<byte[]> queue = new Queue<byte[]>();
|
|
if (save.ContainsKey(-1L))
|
|
{
|
|
List<long> list = new List<long>();
|
|
queue.Enqueue(save[-1L]);
|
|
int num2 = save.Count;
|
|
long[] array = save.Keys.OrderBy((long zn) => zn).ToArray();
|
|
foreach (long num4 in array)
|
|
{
|
|
if (num2-- <= 0)
|
|
{
|
|
break;
|
|
}
|
|
if (num4 != -1)
|
|
{
|
|
if (save.TryGetValue(num4, out var value))
|
|
{
|
|
queue.Enqueue(value);
|
|
}
|
|
list.Add(num4);
|
|
}
|
|
}
|
|
Save(queue);
|
|
foreach (long item in list)
|
|
{
|
|
save.TryRemove(item, out var _);
|
|
}
|
|
}
|
|
}
|
|
Thread.Sleep(1000);
|
|
}
|
|
}
|
|
|
|
private unsafe void Save(Queue<byte[]> sav)
|
|
{
|
|
if (Program.FrameRate != fps)
|
|
{
|
|
fps = Program.FrameRate;
|
|
}
|
|
if (!Settings.Default.SaveVideo || sav.Count <= 1)
|
|
{
|
|
return;
|
|
}
|
|
save_video_flag = true;
|
|
Form1.forListing.Enqueue("Начало записи.");
|
|
try
|
|
{
|
|
AVCodecContext* ptr = ffmpeg.avcodec_alloc_context3(codec);
|
|
ptr->width = 1280;
|
|
ptr->height = 720;
|
|
bool flag = false;
|
|
string text = Directory.GetCurrentDirectory() + "\\video\\";
|
|
if (!Directory.Exists(text))
|
|
{
|
|
Directory.CreateDirectory(text);
|
|
}
|
|
string text2 = $"{DateTime.Now.Ticks}.mp4";
|
|
byte[] first = sav.Dequeue();
|
|
Form1.forListing.Enqueue(text2);
|
|
text2 = text + text2;
|
|
AVOutputFormat* oformat = ffmpeg.av_guess_format(null, text2, null);
|
|
AVFormatContext* ptr2 = ffmpeg.avformat_alloc_context();
|
|
ptr2->oformat = oformat;
|
|
AVStream* ptr3 = ffmpeg.avformat_new_stream(ptr2, codec);
|
|
ptr3->codec = ptr;
|
|
ptr3->time_base.den = fps;
|
|
ptr3->time_base.num = 1;
|
|
ptr3->codec->time_base.den = fps;
|
|
ptr3->codec->time_base.num = 1;
|
|
if (ffmpeg.avio_open(&ptr2->pb, text2, 3) < 0)
|
|
{
|
|
Console.WriteLine("Cannot open file");
|
|
}
|
|
ffmpeg.avformat_write_header(ptr2, null);
|
|
AVPacket aVPacket = default(AVPacket);
|
|
ffmpeg.av_init_packet(&aVPacket);
|
|
long num = 1L;
|
|
while (sav.Count > 0)
|
|
{
|
|
byte[] array = sav.Dequeue();
|
|
if (!flag && array[0] == 1)
|
|
{
|
|
flag = true;
|
|
}
|
|
if (!flag)
|
|
{
|
|
continue;
|
|
}
|
|
byte[] array2 = first.Concat(array.Skip(1)).ToArray();
|
|
fixed (byte* data = &array2[0])
|
|
{
|
|
aVPacket.data = (sbyte*)data;
|
|
aVPacket.size = array2.Length;
|
|
aVPacket.pts = ffmpeg.av_rescale_q(num++, ptr3->codec->time_base, ptr3->time_base);
|
|
ffmpeg.av_interleaved_write_frame(ptr2, &aVPacket);
|
|
}
|
|
}
|
|
ffmpeg.av_free_packet(&aVPacket);
|
|
ffmpeg.av_write_trailer(ptr2);
|
|
for (int i = 0; i < ptr2->nb_streams; i++)
|
|
{
|
|
ffmpeg.av_freep(&ptr2->streams[i]->codec);
|
|
ffmpeg.av_freep(ptr2->streams + i);
|
|
}
|
|
ffmpeg.avio_close(ptr2->pb);
|
|
ffmpeg.av_free(ptr2);
|
|
ffmpeg.avcodec_close(ptr);
|
|
Form1.forListing.Enqueue("Завершение записи.");
|
|
}
|
|
catch (AccessViolationException)
|
|
{
|
|
}
|
|
catch (Exception value)
|
|
{
|
|
Console.WriteLine(value);
|
|
}
|
|
save_video_flag = false;
|
|
}
|
|
|
|
public unsafe Decoder()
|
|
{
|
|
Init();
|
|
ffmpeg.av_register_all();
|
|
ffmpeg.avcodec_register_all();
|
|
codec = ffmpeg.avcodec_find_decoder(AVCodecID.AV_CODEC_ID_H264);
|
|
if (codec == null)
|
|
{
|
|
throw new ApplicationException("Unsupported codec");
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
savevideo();
|
|
Thread.Sleep(300);
|
|
try
|
|
{
|
|
startSaveVideo = false;
|
|
Queue<byte[]> queue = new Queue<byte[]>();
|
|
if (!save.ContainsKey(-1L))
|
|
{
|
|
return;
|
|
}
|
|
queue.Enqueue(save[-1L]);
|
|
foreach (KeyValuePair<long, byte[]> item in save.OrderBy((KeyValuePair<long, byte[]> zn) => zn.Key))
|
|
{
|
|
if (item.Key != -1)
|
|
{
|
|
queue.Enqueue(item.Value);
|
|
save.TryRemove(item.Key, out var _);
|
|
}
|
|
}
|
|
Save(queue);
|
|
}
|
|
catch (Exception value2)
|
|
{
|
|
Console.WriteLine(value2);
|
|
}
|
|
}
|
|
|
|
~Decoder()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
private void savevideo()
|
|
{
|
|
if (save_buff.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
lock (block)
|
|
{
|
|
try
|
|
{
|
|
string arg = Directory.GetCurrentDirectory() + "\\video\\";
|
|
FileStream fileStream = new FileStream($"{arg}{DateTime.Now:yyyyMMddHHmmss}.vs", FileMode.CreateNew);
|
|
fileStream.WriteByte(0);
|
|
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
|
|
byte[] result;
|
|
while (save_buff.TryDequeue(out result))
|
|
{
|
|
binaryWriter.Write(result.Count());
|
|
binaryWriter.Write(result);
|
|
}
|
|
binaryWriter.Flush();
|
|
binaryWriter.Close();
|
|
fileStream.Close();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addFrame(long num, long endkey, byte[] conf, byte[] mas)
|
|
{
|
|
Fram value = new Fram(num, endkey, conf.Concat(mas).ToArray());
|
|
frames.TryAdd(num, value);
|
|
if (save_video)
|
|
{
|
|
if (save_buff.Count == 0)
|
|
{
|
|
save_buff.Enqueue(conf);
|
|
}
|
|
byte[] first = ((value.key != value.num) ? new byte[1] : new byte[1] { 1 });
|
|
save_buff.Enqueue(first.Union(value.data).ToArray());
|
|
}
|
|
if (save_buff.Count > 14400)
|
|
{
|
|
new Thread((ThreadStart)delegate
|
|
{
|
|
savevideo();
|
|
}).Start();
|
|
}
|
|
if (!save.ContainsKey(-1L))
|
|
{
|
|
save.TryAdd(-1L, conf);
|
|
}
|
|
save.TryAdd(num, new byte[1] { (byte)((num == endkey) ? 1u : 0u) }.Concat(mas).ToArray());
|
|
endAddSaveVideo = DateTime.Now;
|
|
outstr.Enqueue($"{num};{endkey};{mas.Length};{DateTime.Now:HH:mm:ss.fffffff}");
|
|
num++;
|
|
frameForFPS++;
|
|
if (timeForFPS == DateTime.MinValue)
|
|
{
|
|
timeForFPS = DateTime.Now;
|
|
}
|
|
if ((DateTime.Now - timeForFPS).TotalSeconds > 1.0)
|
|
{
|
|
double item = (double)frameForFPS / (DateTime.Now - timeForFPS).TotalSeconds;
|
|
fpsAver.Enqueue(item);
|
|
FPS = fpsAver.Average();
|
|
if (fpsAver.Count > 60)
|
|
{
|
|
fpsAver.Dequeue();
|
|
}
|
|
frameForFPS = 0;
|
|
timeForFPS = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
public void AddFrameArray(byte[] buf)
|
|
{
|
|
endFrame = DateTime.Now;
|
|
DateTime now = DateTime.Now;
|
|
try
|
|
{
|
|
long num = BitConverter.ToInt32(buf, 0);
|
|
long num2 = num;
|
|
configN = buf.Skip(8).Take(BitConverter.ToInt32(buf, 4)).ToArray();
|
|
buf = buf.Skip(configN.Length + 8).ToArray();
|
|
int num3 = 0;
|
|
while (num3 < buf.Length)
|
|
{
|
|
byte[] array = buf.Skip(num3 + 4).Take(BitConverter.ToInt32(buf, num3)).ToArray();
|
|
num3 += array.Length + 4;
|
|
addFrame(num2, num, configN, array);
|
|
num2++;
|
|
}
|
|
TrimBuffer();
|
|
}
|
|
catch (Exception value)
|
|
{
|
|
Console.WriteLine(value);
|
|
}
|
|
Form1.t_Frame.Enqueue((DateTime.Now - now).TotalMilliseconds);
|
|
}
|
|
|
|
public void AddNNFrame(byte[] buf)
|
|
{
|
|
endFrame = DateTime.Now;
|
|
DateTime now = DateTime.Now;
|
|
_ = DateTime.Now.Ticks;
|
|
if (buf[0] == 2)
|
|
{
|
|
outstr.Enqueue("!");
|
|
configN = buf.Skip(1).ToArray();
|
|
}
|
|
else if (configN != null)
|
|
{
|
|
long num = BitConverter.ToInt32(buf.Skip(1).Take(4).Reverse()
|
|
.ToArray(), 0);
|
|
byte[] array = new byte[0];
|
|
if (buf[0] == 1)
|
|
{
|
|
endkey = num;
|
|
array = buf.Skip(5).ToArray();
|
|
}
|
|
else
|
|
{
|
|
endkey = BitConverter.ToInt32(buf.Skip(5).Take(4).Reverse()
|
|
.ToArray(), 0);
|
|
array = buf.Skip(9).ToArray();
|
|
}
|
|
outstr.Enqueue($"{num};{endkey};{DateTime.Now:HH:mm:ss.fffffff}");
|
|
try
|
|
{
|
|
addFrame(num, endkey, configN, array);
|
|
TrimBuffer();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(num);
|
|
Console.WriteLine(ex.Message);
|
|
Console.WriteLine(ex.StackTrace);
|
|
}
|
|
Form1.t_Frame.Enqueue((DateTime.Now - now).TotalMilliseconds);
|
|
}
|
|
}
|
|
|
|
private void TrimBuffer()
|
|
{
|
|
while (frames.Count > 18000)
|
|
{
|
|
frames.TryRemove(frames.Keys.Min(), out var _);
|
|
}
|
|
}
|
|
|
|
public unsafe Bitmap GetImg(long numb)
|
|
{
|
|
//IL_025f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0265: Expected O, but got Unknown
|
|
if (save_video_flag)
|
|
{
|
|
return null;
|
|
}
|
|
Bitmap result = null;
|
|
_ = DateTime.Now;
|
|
try
|
|
{
|
|
AVCodecContext* ptr = ffmpeg.avcodec_alloc_context3(codec);
|
|
if ((codec->capabilities & 8) == 8)
|
|
{
|
|
ptr->flags |= 65536;
|
|
}
|
|
int num = ffmpeg.avcodec_open2(ptr, codec, null);
|
|
if (num < 0)
|
|
{
|
|
Console.WriteLine("Ошибка инициализации кодека: " + num);
|
|
}
|
|
try
|
|
{
|
|
if (frames.ContainsKey(numb))
|
|
{
|
|
Fram fram = frames[numb];
|
|
if (frames.ContainsKey(fram.key))
|
|
{
|
|
GetImgEndFrame = frames[numb].key;
|
|
int num2 = 0;
|
|
AVFrame* ptr2 = ffmpeg.av_frame_alloc();
|
|
int num3 = 0;
|
|
Fram value;
|
|
for (long num4 = GetImgEndFrame; num4 <= numb && frames.TryGetValue(num4, out value); num4++)
|
|
{
|
|
fixed (byte* data = value.data)
|
|
{
|
|
AVPacket aVPacket = default(AVPacket);
|
|
_ = ref aVPacket;
|
|
ffmpeg.av_init_packet(&aVPacket);
|
|
aVPacket.data = (sbyte*)data;
|
|
aVPacket.size = value.data.Length;
|
|
ffmpeg.avcodec_decode_video2(ptr, ptr2, &num2, &aVPacket);
|
|
ffmpeg.av_free_packet(&aVPacket);
|
|
num3++;
|
|
GetImgEndFrame = num4;
|
|
}
|
|
}
|
|
if (num2 == 1)
|
|
{
|
|
dec = true;
|
|
SwsContext* intPtr = ffmpeg.sws_getCachedContext(null, ptr->coded_width, ptr->coded_height, ptr->pix_fmt, ptr->coded_width, ptr->coded_height, AVPixelFormat.AV_PIX_FMT_BGR24, 1, null, null, null);
|
|
if (pConvertedFrameBuffer != null)
|
|
{
|
|
ffmpeg.av_free(pConvertedFrameBuffer);
|
|
}
|
|
AVFrame* ptr3 = ffmpeg.av_frame_alloc();
|
|
pConvertedFrameBuffer = (sbyte*)ffmpeg.av_malloc((ulong)ffmpeg.avpicture_get_size(AVPixelFormat.AV_PIX_FMT_BGR24, ptr->coded_width, ptr->coded_height));
|
|
ffmpeg.avpicture_fill((AVPicture*)ptr3, pConvertedFrameBuffer, AVPixelFormat.AV_PIX_FMT_BGR24, ptr->coded_width, ptr->coded_height);
|
|
ffmpeg.sws_scale(intPtr, &ptr2->data0, ptr2->linesize, 0, ptr->height, &ptr3->data0, ptr3->linesize);
|
|
IntPtr intPtr2 = new IntPtr(ptr3->data0);
|
|
result = new Bitmap(ptr->coded_width, ptr->coded_height, *ptr3->linesize, (PixelFormat)137224, intPtr2);
|
|
ffmpeg.av_free(ptr3);
|
|
ffmpeg.sws_freeContext(intPtr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex.Message);
|
|
Console.WriteLine(ex.StackTrace);
|
|
}
|
|
ffmpeg.avcodec_close(ptr);
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
Console.WriteLine(ex2.Message);
|
|
Console.WriteLine(ex2.StackTrace);
|
|
}
|
|
return result;
|
|
}
|
|
}
|