From 300e9abc696849d87d61c3030ccd88d4d4ab378f Mon Sep 17 00:00:00 2001 From: "Kim,Hwan" Date: Mon, 3 Oct 2022 15:26:23 +0900 Subject: [PATCH] [#37] Fixes an issue that caused loud noises to be recorded in the audio when recording at 144fps. --- ScreenRecorder/AudioSource/AudioMixer.cs | 133 +++++--- ScreenRecorder/Encoder/Encoder.cs | 384 ++++++++++++---------- ScreenRecorder/Properties/AssemblyInfo.cs | 4 +- Setup/Setup.vdproj | 22 +- 4 files changed, 313 insertions(+), 230 deletions(-) diff --git a/ScreenRecorder/AudioSource/AudioMixer.cs b/ScreenRecorder/AudioSource/AudioMixer.cs index 99db0fa..22b478a 100644 --- a/ScreenRecorder/AudioSource/AudioMixer.cs +++ b/ScreenRecorder/AudioSource/AudioMixer.cs @@ -7,106 +7,126 @@ namespace ScreenRecorder.AudioSource { + /// + /// Audio Mixer (2Ch, 16bit, 48000Hz) + /// public class AudioMixer : IAudioSource, IDisposable { - private readonly IAudioSource[] audioSources; - private readonly CircularBuffer circularMixerBuffer; + #region Fields - private Thread mixerThread, renderThread; - private ManualResetEvent needToStop; + private readonly IAudioSource[] _audioSources; + private readonly CircularBuffer _circularMixerBuffer; + private readonly int _samplesPerFrame; + private readonly int _samplesBytesPerFrame; + private readonly int _framesPerAdditinalSample; + + private Thread _mixerThread, _renderThread; + private ManualResetEvent _needToStop; + + #endregion + + #region Constructors public AudioMixer(params IAudioSource[] audioSources) { - this.audioSources = audioSources; - var framePerBytes = (int)(48000.0d / VideoClockEvent.Framerate * 4); - circularMixerBuffer = new CircularBuffer(framePerBytes * 6); + _audioSources = audioSources; + _samplesPerFrame = (int)(48000.0d / VideoClockEvent.Framerate); + _samplesBytesPerFrame = _samplesPerFrame * 2 * 2; // 2Ch, 16bit + + var remainingSamples = 48000 - (_samplesPerFrame * VideoClockEvent.Framerate); + _framesPerAdditinalSample = remainingSamples != 0 ? VideoClockEvent.Framerate / remainingSamples : 0; - needToStop = new ManualResetEvent(false); + _circularMixerBuffer = new CircularBuffer(_samplesBytesPerFrame * 6); - mixerThread = new Thread(MixerThreadHandler) { Name = "AudioMixer_Mixer", IsBackground = true }; - mixerThread.Start(); + _needToStop = new ManualResetEvent(false); - renderThread = new Thread(RenderThreadHandler) { Name = "AudioMixer_Render", IsBackground = true }; - renderThread.Start(); + _mixerThread = new Thread(MixerThreadHandler) { Name = "AudioMixer_Mixer", IsBackground = true }; + _mixerThread.Start(); + + _renderThread = new Thread(RenderThreadHandler) { Name = "AudioMixer_Render", IsBackground = true }; + _renderThread.Start(); } - public event NewAudioPacketEventHandler NewAudioPacket; + #endregion + + #region Helpers + + [DllImport("Kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)] + internal static extern void ZeroMemory(IntPtr dest, IntPtr size); public void Dispose() { - if (needToStop != null) + if (_needToStop != null) { - needToStop.Set(); + _needToStop.Set(); } - if (mixerThread != null) + if (_mixerThread != null) { - if (mixerThread.IsAlive && !mixerThread.Join(500)) + if (_mixerThread.IsAlive && !_mixerThread.Join(500)) { - mixerThread.Abort(); + _mixerThread.Abort(); } - mixerThread = null; + _mixerThread = null; } - if (renderThread != null) + if (_renderThread != null) { - if (renderThread.IsAlive && !renderThread.Join(500)) + if (_renderThread.IsAlive && !_renderThread.Join(500)) { - renderThread.Abort(); + _renderThread.Abort(); } - renderThread = null; + _renderThread = null; } - if (needToStop != null) + if (_needToStop != null) { - needToStop.Close(); + _needToStop.Close(); } - needToStop = null; + _needToStop = null; } - [DllImport("Kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)] - internal static extern void ZeroMemory(IntPtr dest, IntPtr size); - private void MixerThreadHandler() { - var framePerBytes = (int)(48000.0d / VideoClockEvent.Framerate * 4); - - var sources = audioSources.Select(source => new AudioSourceResampler(source, 2, SampleFormat.S16, 48000)) + var sources = _audioSources.Select(source => new AudioSourceResampler(source, 2, SampleFormat.S16, 48000)) .ToArray(); - var sample = Marshal.AllocHGlobal(framePerBytes); - var mixSample = Marshal.AllocHGlobal(framePerBytes); + var sample = Marshal.AllocHGlobal(_samplesBytesPerFrame + 4); + var mixSample = Marshal.AllocHGlobal(_samplesBytesPerFrame + 4); using (var systemClockEvent = new VideoClockEvent()) { - while (!needToStop.WaitOne(0, false)) + long frames = 0; + while (!_needToStop.WaitOne(0, false)) { if (systemClockEvent.WaitOne(10)) { - var count = sources[0].Buffer.Read(mixSample, framePerBytes); - if (count < framePerBytes) + var samplesBytesPerFrame = _samplesBytesPerFrame + (frames++ % 3 == 0 ? 4 : 0); + + var count = sources[0].Buffer.Read(mixSample, samplesBytesPerFrame); + if (count < samplesBytesPerFrame) { - ZeroMemory(mixSample + count, new IntPtr(framePerBytes - count)); + ZeroMemory(mixSample + count, new IntPtr(samplesBytesPerFrame - count)); } for (var i = 1; i < sources.Length; i++) { if (sources[i].IsValidBuffer) { - count = sources[i].Buffer.Read(sample, framePerBytes); - if (count < framePerBytes) + count = sources[i].Buffer.Read(sample, samplesBytesPerFrame); + if (count < samplesBytesPerFrame) { - ZeroMemory(sample + count, new IntPtr(framePerBytes - count)); + ZeroMemory(sample + count, new IntPtr(samplesBytesPerFrame - count)); } MixStereoSamples(sample, mixSample, mixSample, count / 4); } } - circularMixerBuffer.Write(mixSample, 0, framePerBytes); + _circularMixerBuffer.Write(mixSample, 0, samplesBytesPerFrame); } } } @@ -151,20 +171,20 @@ private void MixStereoSamples(IntPtr sample1, IntPtr sample2, IntPtr mix, int sa private void RenderThreadHandler() { - var samples = (int)(48000.0d / VideoClockEvent.Framerate); - - var mixerAudioBuffer = Marshal.AllocHGlobal(samples * 2 * 2); // 16bit 2channels + var mixerAudioBuffer = Marshal.AllocHGlobal(_samplesBytesPerFrame + 4); // 16bit 2channels using (var systemClockEvent = new VideoClockEvent()) { - while (!needToStop.WaitOne(0, false)) + long frames = 0; + while (!_needToStop.WaitOne(0, false)) { if (systemClockEvent.WaitOne(10)) { - if (circularMixerBuffer.Count >= samples * 2 * 2) + var samplesBytesPerFrame = _samplesBytesPerFrame + (frames++ % 3 == 0 ? 4 : 0); + + if (_circularMixerBuffer.Count >= samplesBytesPerFrame) { - circularMixerBuffer.Read(mixerAudioBuffer, samples * 2 * 2); - NewAudioPacket?.Invoke(this, - new NewAudioPacketEventArgs(48000, 2, SampleFormat.S16, samples, mixerAudioBuffer)); + _circularMixerBuffer.Read(mixerAudioBuffer, samplesBytesPerFrame); + OnNewAudioPacket(new NewAudioPacketEventArgs(48000, 2, SampleFormat.S16, samplesBytesPerFrame / 4, mixerAudioBuffer)); } } } @@ -172,5 +192,18 @@ private void RenderThreadHandler() Marshal.FreeHGlobal(mixerAudioBuffer); } + + #endregion + + #region Events + + public event NewAudioPacketEventHandler NewAudioPacket; + + public void OnNewAudioPacket(NewAudioPacketEventArgs args) + { + NewAudioPacket?.Invoke(this, args); + } + + #endregion } } diff --git a/ScreenRecorder/Encoder/Encoder.cs b/ScreenRecorder/Encoder/Encoder.cs index 0976c99..8c57098 100644 --- a/ScreenRecorder/Encoder/Encoder.cs +++ b/ScreenRecorder/Encoder/Encoder.cs @@ -10,6 +10,7 @@ namespace ScreenRecorder.Encoder { public delegate void EncoderStoppedEventHandler(object sender, EncoderStoppedEventArgs eventArgs); + public class EncoderStoppedEventArgs : EventArgs { public ulong VideoFramesCount { get; private set; } @@ -18,9 +19,9 @@ public class EncoderStoppedEventArgs : EventArgs public EncoderStoppedEventArgs(ulong videoFramesCount, ulong audioSamplesCount, string url) { - this.VideoFramesCount = videoFramesCount; - this.AudioSamplesCount = audioSamplesCount; - this.Url = url; + VideoFramesCount = videoFramesCount; + AudioSamplesCount = audioSamplesCount; + Url = url; } } @@ -28,80 +29,103 @@ public class Encoder : NotifyPropertyBase, IDisposable { private class MediaBuffer : IDisposable { - private object videoSyncObject = new object(); - private object audioSyncObject = new object(); - private IVideoSource videoSource; - private IAudioSource audioSource; - private bool isDisposed = false; + #region Fields + + private readonly object _videoSyncObject = new object(); + private readonly object _audioSyncObject = new object(); + private readonly IVideoSource _videoSource; + private readonly IAudioSource _audioSource; + private bool _isDisposed = false; + + private readonly ConcurrentQueue _srcVideoFrameQueue; - private ConcurrentQueue srcVideoFrameQueue; + private readonly ConcurrentQueue _videoFrameQueue; + private readonly ConcurrentQueue _audioFrameQueue; + private readonly ManualResetEvent _enableEvent; - private ConcurrentQueue videoFrameQueue; - private ConcurrentQueue audioFrameQueue; + private Thread _videoWorkerThread; + private Thread _audioWorkerThread; + private ManualResetEvent _needToStop; - private ManualResetEvent enableEvent; + private readonly CircularBuffer _srcAudioCircularBuffer; + private Resampler _resampler; - private Thread videoWorkerThread; - private Thread audioWorkerThread; - private ManualResetEvent needToStop; + private readonly int _samplesPerFrame; + private readonly int _samplesBytesPerFrame; + private readonly int _framesPerAdditinalSample; - private CircularBuffer srcAudioCircularBuffer; - private Resampler resampler; + #endregion + + #region Constructors public MediaBuffer(IVideoSource videoSource, IAudioSource audioSource) { - this.enableEvent = new ManualResetEvent(false); - this.videoSource = videoSource; - this.audioSource = audioSource; + _enableEvent = new ManualResetEvent(false); + _videoSource = videoSource; + _audioSource = audioSource; - if (this.videoSource != null) + if (_videoSource != null) { - this.videoFrameQueue = new ConcurrentQueue(); - this.srcVideoFrameQueue = new ConcurrentQueue(); - this.videoSource.NewVideoFrame += VideoSource_NewVideoFrame; - videoWorkerThread = new Thread(new ThreadStart(VideoWorkerThreadHandler)) { IsBackground = true }; + _videoFrameQueue = new ConcurrentQueue(); + _srcVideoFrameQueue = new ConcurrentQueue(); + _videoSource.NewVideoFrame += VideoSource_NewVideoFrame; + _videoWorkerThread = new Thread(new ThreadStart(VideoWorkerThreadHandler)) { IsBackground = true }; } - if (this.audioSource != null) + if (_audioSource != null) { - resampler = new Resampler(); - int framePerBytes = (int)(48000.0d / VideoClockEvent.Framerate * 4); + _resampler = new Resampler(); + + _samplesPerFrame = (int)(48000.0d / VideoClockEvent.Framerate); + _samplesBytesPerFrame = _samplesPerFrame * 2 * 2; // 2Ch, 16bit - this.srcAudioCircularBuffer = new CircularBuffer(framePerBytes * 15); - this.audioFrameQueue = new ConcurrentQueue(); - this.audioSource.NewAudioPacket += AudioSource_NewAudioPacket; - audioWorkerThread = new Thread(new ThreadStart(AudioWorkerThreadHandler)) { IsBackground = true }; + var remainingSamples = 48000 - (_samplesPerFrame * VideoClockEvent.Framerate); + _framesPerAdditinalSample = remainingSamples != 0 ? VideoClockEvent.Framerate / remainingSamples : 0; + + _srcAudioCircularBuffer = new CircularBuffer(_samplesBytesPerFrame * 15); + _audioFrameQueue = new ConcurrentQueue(); + _audioSource.NewAudioPacket += AudioSource_NewAudioPacket; + _audioWorkerThread = new Thread(new ThreadStart(AudioWorkerThreadHandler)) { IsBackground = true }; } - needToStop = new ManualResetEvent(false); + _needToStop = new ManualResetEvent(false); - videoWorkerThread?.Start(); - audioWorkerThread?.Start(); + _videoWorkerThread?.Start(); + _audioWorkerThread?.Start(); } + #endregion + + #region Helpers + private void AudioWorkerThreadHandler() { - int samples = (int)(48000.0d / VideoClockEvent.Framerate); + int samplesPerAudioFrame = _samplesPerFrame; // Keep the minimum number of samples at 1600 (Aac codec has a minimum number of samples, so less than this will cause problems) // I tried to process it on the encoder, but it's easier to implement if I just supply a lot of samples. - int skipFrames = (int)(Math.Ceiling(1600.0d / samples) - 1); - samples *= (skipFrames + 1); + int skipFrames = (int)(Math.Ceiling(1600.0d / samplesPerAudioFrame) - 1); + samplesPerAudioFrame *= skipFrames + 1; + + var samplesBytesPerFrame = samplesPerAudioFrame * 2 * 2; long skipCount = skipFrames; - IntPtr audioBuffer = Marshal.AllocHGlobal(samples * 4); + IntPtr audioBuffer = Marshal.AllocHGlobal(samplesBytesPerFrame + (VideoClockEvent.Framerate * 2 * 2)); using (VideoClockEvent videoClockEvent = new VideoClockEvent()) { - while (!needToStop.WaitOne(0, false)) + long frames = 0, lastReadedFrames = 0; + while (!_needToStop.WaitOne(0, false)) { if (videoClockEvent.WaitOne(10)) { - if (!(enableEvent?.WaitOne(0, false) ?? true)) + frames++; + + if (!(_enableEvent?.WaitOne(0, false) ?? true)) continue; /// Frames can stack if the PC momentarily slows down and the encoding speed drops below x1. /// This will cause the recorded image to be disconnected, so it is specified that it can be buffered up to 300 frames. - if (audioFrameQueue.Count > 300) + if (_audioFrameQueue.Count > 300) { continue; } @@ -115,21 +139,26 @@ private void AudioWorkerThreadHandler() skipCount = skipFrames; } - if (srcAudioCircularBuffer.Count >= (samples * 4)) + var needAdditinalSamples = _framesPerAdditinalSample != 0 ? Math.Min(VideoClockEvent.Framerate, (int)(lastReadedFrames - frames) % _framesPerAdditinalSample) : 0; + var needSamplesBytes = samplesBytesPerFrame + (needAdditinalSamples * 4); + + if (_srcAudioCircularBuffer.Count >= needSamplesBytes) { - srcAudioCircularBuffer.Read(audioBuffer, (samples * 4)); + _srcAudioCircularBuffer.Read(audioBuffer, needSamplesBytes); - AudioFrame audioFrame = new AudioFrame(48000, 2, SampleFormat.S16, samples); + AudioFrame audioFrame = new AudioFrame(48000, 2, SampleFormat.S16, needSamplesBytes / 4); audioFrame.FillFrame(audioBuffer); - audioFrameQueue.Enqueue(audioFrame); + _audioFrameQueue.Enqueue(audioFrame); } else { - AudioFrame audioFrame = new AudioFrame(48000, 2, SampleFormat.S16, samples); + AudioFrame audioFrame = new AudioFrame(48000, 2, SampleFormat.S16, needSamplesBytes / 4); audioFrame.ClearFrame(); - audioFrameQueue.Enqueue(audioFrame); + _audioFrameQueue.Enqueue(audioFrame); } + + lastReadedFrames = frames; } } } @@ -142,44 +171,44 @@ private void VideoWorkerThreadHandler() using (VideoClockEvent videoClockEvent = new VideoClockEvent()) { - while (!needToStop.WaitOne(0, false)) + while (!_needToStop.WaitOne(0, false)) { if (videoClockEvent.WaitOne(10)) { - if (!(enableEvent?.WaitOne(0, false) ?? true)) + if (!(_enableEvent?.WaitOne(0, false) ?? true)) continue; /// Frames can stack if the PC momentarily slows down and the encoding speed drops below x1. /// This will cause the recorded image to be disconnected, so it is specified that it can be buffered up to 300 frames. - if (videoFrameQueue.Count > 300) // max buffer + if (_videoFrameQueue.Count > 300) // max buffer { continue; } - if (srcVideoFrameQueue.TryDequeue(out VideoFrame videoFrame)) + if (_srcVideoFrameQueue.TryDequeue(out VideoFrame videoFrame)) { - if (srcVideoFrameQueue.Count > 3) + if (_srcVideoFrameQueue.Count > 3) { for (int i = 0; i < 3; i++) { - if (srcVideoFrameQueue.TryDequeue(out VideoFrame temp)) + if (_srcVideoFrameQueue.TryDequeue(out VideoFrame temp)) temp.Dispose(); } } lastVideoFrame?.Dispose(); lastVideoFrame = new VideoFrame(videoFrame); - videoFrameQueue.Enqueue(videoFrame); + _videoFrameQueue.Enqueue(videoFrame); } else if (lastVideoFrame != null) { VideoFrame clone = new VideoFrame(lastVideoFrame); - videoFrameQueue.Enqueue(lastVideoFrame); + _videoFrameQueue.Enqueue(lastVideoFrame); lastVideoFrame = clone; } else { - videoFrameQueue.Enqueue(new VideoFrame(1920, 1080, PixelFormat.RGB24)); + _videoFrameQueue.Enqueue(new VideoFrame(1920, 1080, PixelFormat.RGB24)); } } } @@ -189,43 +218,43 @@ private void VideoWorkerThreadHandler() public void Start() { - if (enableEvent != null) + if (_enableEvent != null) { - while (audioFrameQueue?.Count > 0) + while (_audioFrameQueue?.Count > 0) { - if (audioFrameQueue.TryDequeue(out AudioFrame audioFrame)) + if (_audioFrameQueue.TryDequeue(out AudioFrame audioFrame)) audioFrame.Dispose(); } - while (videoFrameQueue?.Count > 0) + while (_videoFrameQueue?.Count > 0) { - if (videoFrameQueue.TryDequeue(out VideoFrame videoFrame)) + if (_videoFrameQueue.TryDequeue(out VideoFrame videoFrame)) videoFrame.Dispose(); } - if (!enableEvent.WaitOne(0, false)) - enableEvent.Set(); + if (!_enableEvent.WaitOne(0, false)) + _enableEvent.Set(); } } public void Stop() { - if (enableEvent != null) + if (_enableEvent != null) { - if (enableEvent.WaitOne(0, false)) + if (_enableEvent.WaitOne(0, false)) { - enableEvent.Reset(); + _enableEvent.Reset(); } } } private void VideoSource_NewVideoFrame(object sender, NewVideoFrameEventArgs eventArgs) { - lock (videoSyncObject) + lock (_videoSyncObject) { - if (isDisposed) + if (_isDisposed) return; - if (enableEvent != null && !enableEvent.WaitOne(0, false)) + if (_enableEvent != null && !_enableEvent.WaitOne(0, false)) return; VideoFrame videoFrame = new VideoFrame(eventArgs.Width, eventArgs.Height, eventArgs.PixelFormat); @@ -237,91 +266,91 @@ private void VideoSource_NewVideoFrame(object sender, NewVideoFrameEventArgs eve { videoFrame.FillFrame(eventArgs.DataPointer, eventArgs.Stride); } - srcVideoFrameQueue.Enqueue(videoFrame); + _srcVideoFrameQueue.Enqueue(videoFrame); } } private void AudioSource_NewAudioPacket(object sender, NewAudioPacketEventArgs eventArgs) { - lock (audioSyncObject) + lock (_audioSyncObject) { - if (isDisposed) + if (_isDisposed) return; - if (enableEvent != null && !enableEvent.WaitOne(0, false)) + if (_enableEvent != null && !_enableEvent.WaitOne(0, false)) return; if (eventArgs.Channels != 2 || eventArgs.SampleFormat != SampleFormat.S16 || eventArgs.SampleRate != 48000) { - resampler.Resampling(eventArgs.Channels, eventArgs.SampleFormat, eventArgs.SampleRate, - 2, SampleFormat.S16, 48000, eventArgs.DataPointer, eventArgs.Samples, out IntPtr destData, out int destSamples); + _resampler.Resampling(eventArgs.Channels, eventArgs.SampleFormat, eventArgs.SampleRate, + 2, SampleFormat.S16, 48000, eventArgs.DataPointer, eventArgs.Samples, out var destData, out var destSamples); - srcAudioCircularBuffer.Write(destData, 0, destSamples * 4); + _srcAudioCircularBuffer.Write(destData, 0, destSamples * 4); } else { - srcAudioCircularBuffer.Write(eventArgs.DataPointer, 0, eventArgs.Samples * 4); + _srcAudioCircularBuffer.Write(eventArgs.DataPointer, 0, eventArgs.Samples * 4); } } } public void Dispose() { - needToStop?.Set(); - if (videoWorkerThread != null) + _needToStop?.Set(); + if (_videoWorkerThread != null) { - if (videoWorkerThread.IsAlive && !videoWorkerThread.Join(2000)) - videoWorkerThread.Abort(); + if (_videoWorkerThread.IsAlive && !_videoWorkerThread.Join(2000)) + _videoWorkerThread.Abort(); - videoWorkerThread = null; + _videoWorkerThread = null; } - if (audioWorkerThread != null) + if (_audioWorkerThread != null) { - if (audioWorkerThread.IsAlive && !audioWorkerThread.Join(500)) - audioWorkerThread.Abort(); + if (_audioWorkerThread.IsAlive && !_audioWorkerThread.Join(500)) + _audioWorkerThread.Abort(); - audioWorkerThread = null; + _audioWorkerThread = null; } - needToStop?.Close(); - needToStop = null; + _needToStop?.Close(); + _needToStop = null; - lock (videoSyncObject) + lock (_videoSyncObject) { - lock (audioSyncObject) + lock (_audioSyncObject) { - if (this.videoSource != null) - this.videoSource.NewVideoFrame -= VideoSource_NewVideoFrame; - while (srcVideoFrameQueue?.Count > 0) + if (_videoSource != null) + _videoSource.NewVideoFrame -= VideoSource_NewVideoFrame; + while (_srcVideoFrameQueue?.Count > 0) { - if (srcVideoFrameQueue.TryDequeue(out VideoFrame videoFrame)) + if (_srcVideoFrameQueue.TryDequeue(out VideoFrame videoFrame)) videoFrame.Dispose(); } - if (this.audioSource != null) - this.audioSource.NewAudioPacket -= AudioSource_NewAudioPacket; - while (audioFrameQueue?.Count > 0) + if (_audioSource != null) + _audioSource.NewAudioPacket -= AudioSource_NewAudioPacket; + while (_audioFrameQueue?.Count > 0) { - if (audioFrameQueue.TryDequeue(out AudioFrame audioFrame)) + if (_audioFrameQueue.TryDequeue(out AudioFrame audioFrame)) audioFrame.Dispose(); } - while (videoFrameQueue?.Count > 0) + while (_videoFrameQueue?.Count > 0) { - if (videoFrameQueue.TryDequeue(out VideoFrame videoFrame)) + if (_videoFrameQueue.TryDequeue(out VideoFrame videoFrame)) videoFrame.Dispose(); } - resampler?.Dispose(); - resampler = null; + _resampler?.Dispose(); + _resampler = null; - isDisposed = true; + _isDisposed = true; } } } public VideoFrame TryVideoFrameDequeue() { - if (videoFrameQueue != null && videoFrameQueue.TryDequeue(out VideoFrame videoFrame)) + if (_videoFrameQueue != null && _videoFrameQueue.TryDequeue(out VideoFrame videoFrame)) { return videoFrame; } @@ -330,12 +359,14 @@ public VideoFrame TryVideoFrameDequeue() public AudioFrame TryAudioFrameDequeue() { - if (audioFrameQueue != null && audioFrameQueue.TryDequeue(out AudioFrame audioFrame)) + if (_audioFrameQueue != null && _audioFrameQueue.TryDequeue(out AudioFrame audioFrame)) { return audioFrame; } return null; } + + #endregion } private class EncoderArguments @@ -353,78 +384,88 @@ private class EncoderArguments public VideoSize VideoSize { get; set; } } - private ulong videoFramesCount; + #region Bindable Properties + + private ulong _videoFramesCount; + public ulong VideoFramesCount { - get => videoFramesCount; + get => _videoFramesCount; private set { - SetProperty(ref videoFramesCount, value); + SetProperty(ref _videoFramesCount, value); VideoTime = Utils.VideoFramesCountToSeconds(value); } } - private ulong videoTime; + private ulong _videoTime; + public ulong VideoTime { - get => videoTime; + get => _videoTime; private set { - SetProperty(ref videoTime, value); + SetProperty(ref _videoTime, value); } } - private ulong audioSamplesCount; + private ulong _audioSamplesCount; + public ulong AudioSamplesCount { - get => audioSamplesCount; - private set => SetProperty(ref audioSamplesCount, value); + get => _audioSamplesCount; + private set => SetProperty(ref _audioSamplesCount, value); } - private string url; + private string _url; + public string Url { - get => url; - private set => SetProperty(ref url, value); + get => _url; + private set => SetProperty(ref _url, value); } - private bool isStarted = false; + private bool _isStarted = false; + public bool IsStarted { - get => isStarted; + get => _isStarted; private set { - SetProperty(ref isStarted, value); + SetProperty(ref _isStarted, value); } } - private bool isPaused = false; + private bool _isPaused = false; + public bool IsPaused { - get => isPaused; + get => _isPaused; private set { - SetProperty(ref isPaused, value); + SetProperty(ref _isPaused, value); } } - private bool isStopped = true; + private bool _isStopped = true; + public bool IsStopped { - get => isStopped; + get => _isStopped; private set { - SetProperty(ref isStopped, value); + SetProperty(ref _isStopped, value); } } - private EncoderStatus status = EncoderStatus.Stop; + private EncoderStatus _status = EncoderStatus.Stop; + public EncoderStatus Status { - get => status; + get => _status; private set { - if (SetProperty(ref status, value)) + if (SetProperty(ref _status, value)) { IsStarted = (value == EncoderStatus.Start); IsPaused = (value == EncoderStatus.Pause); @@ -437,33 +478,39 @@ public bool IsRunning { get { - if (workerThread != null) + if (_workerThread != null) { - if (workerThread.IsAlive && workerThread.Join(0) == false) + if (_workerThread.IsAlive && _workerThread.Join(0) == false) return true; - workerThread = null; + _workerThread = null; - if (needToStop != null) - needToStop.Close(); - needToStop = null; + if (_needToStop != null) + _needToStop.Close(); + _needToStop = null; } return false; } } - private ulong maximumVideoFramesCount = 0; + private ulong _maximumVideoFramesCount = 0; + public ulong MaximumVideoFramesCount { - get => maximumVideoFramesCount; + get => _maximumVideoFramesCount; set { - SetProperty(ref maximumVideoFramesCount, value); + SetProperty(ref _maximumVideoFramesCount, value); } } - private Thread workerThread = null; - private ManualResetEvent needToStop = null; + #endregion + + #region Helpers + + private Thread _workerThread = null; + + private ManualResetEvent _needToStop = null; public void Start(string format, string url, IVideoSource videoSource, VideoCodec videoCodec, int videoBitrate, VideoSize videoSize, IAudioSource audioSource, AudioCodec audioCodec, int audioBitrate) { @@ -476,9 +523,9 @@ public void Start(string format, string url, IVideoSource videoSource, VideoCode OnEncoderFirstStarting(); - needToStop = new ManualResetEvent(false); - workerThread = new Thread(new ParameterizedThreadStart(WorkerThreadHandler)) { Name = "Encoder", IsBackground = true }; - workerThread.Start(new EncoderArguments() + _needToStop = new ManualResetEvent(false); + _workerThread = new Thread(new ParameterizedThreadStart(WorkerThreadHandler)) { Name = "Encoder", IsBackground = true }; + _workerThread.Start(new EncoderArguments() { VideoSource = videoSource, AudioSource = audioSource, @@ -513,19 +560,19 @@ public void Stop() if (!IsRunning) return; - if (needToStop != null) + if (_needToStop != null) { - needToStop.Set(); + _needToStop.Set(); } - if (workerThread != null) + if (_workerThread != null) { - if (workerThread.IsAlive && !workerThread.Join(3000)) - workerThread.Abort(); - workerThread = null; + if (_workerThread.IsAlive && !_workerThread.Join(3000)) + _workerThread.Abort(); + _workerThread = null; - if (needToStop != null) - needToStop.Close(); - needToStop = null; + if (_needToStop != null) + _needToStop.Close(); + _needToStop = null; } VideoFramesCount = 0; @@ -540,40 +587,40 @@ private void WorkerThreadHandler(object argument) { if (argument is EncoderArguments encoderArguments) { - using (MediaBuffer mediaBuffer = new MediaBuffer(encoderArguments.VideoSource, encoderArguments.AudioCodec == AudioCodec.None ? null : encoderArguments.AudioSource)) + using (var mediaBuffer = new MediaBuffer(encoderArguments.VideoSource, encoderArguments.AudioCodec == AudioCodec.None ? null : encoderArguments.AudioSource)) { - using (MediaWriter mediaWriter = new MediaWriter( - encoderArguments.VideoSize.Width, encoderArguments.VideoSize.Height, (int)VideoClockEvent.Framerate, 1, + using (var mediaWriter = new MediaWriter( + encoderArguments.VideoSize.Width, encoderArguments.VideoSize.Height, VideoClockEvent.Framerate, 1, encoderArguments.VideoCodec, encoderArguments.VideoBitrate, encoderArguments.AudioCodec, encoderArguments.AudioBitrate)) { mediaWriter.Open(encoderArguments.Url, encoderArguments.Format); mediaBuffer.Start(); - while (!needToStop.WaitOne(0, false)) + while (!_needToStop.WaitOne(0, false)) { - VideoFrame videoFrame = mediaBuffer.TryVideoFrameDequeue(); - AudioFrame audioFrame = mediaBuffer.TryAudioFrameDequeue(); + var videoFrame = mediaBuffer.TryVideoFrameDequeue(); + var audioFrame = mediaBuffer.TryAudioFrameDequeue(); if (videoFrame != null || audioFrame != null) { if (videoFrame != null) { - if (status != EncoderStatus.Pause) + if (_status != EncoderStatus.Pause) { mediaWriter.EncodeVideoFrame(videoFrame); VideoFramesCount = mediaWriter.VideoFramesCount; } - if (maximumVideoFramesCount > 0 && maximumVideoFramesCount <= videoFramesCount) + if (_maximumVideoFramesCount > 0 && _maximumVideoFramesCount <= _videoFramesCount) { - needToStop?.Set(); + _needToStop?.Set(); } videoFrame.Dispose(); } if (audioFrame != null) { - if (status != EncoderStatus.Pause) + if (_status != EncoderStatus.Pause) { mediaWriter.EncodeAudioFrame(audioFrame); AudioSamplesCount = mediaWriter.AudioSamplesCount; @@ -584,7 +631,7 @@ private void WorkerThreadHandler(object argument) } else { - if (needToStop.WaitOne(1, false)) + if (_needToStop.WaitOne(1, false)) break; } } @@ -598,7 +645,7 @@ private void WorkerThreadHandler(object argument) } finally { - OnEncoderStopped(new EncoderStoppedEventArgs(videoFramesCount, audioSamplesCount, url)); + OnEncoderStopped(new EncoderStoppedEventArgs(_videoFramesCount, _audioSamplesCount, _url)); VideoFramesCount = 0; AudioSamplesCount = 0; Url = ""; @@ -620,14 +667,17 @@ protected virtual void Dispose(bool disposing) } } + #endregion + #region Events public event EncoderStoppedEventHandler EncoderStopped; + protected virtual void OnEncoderStopped(EncoderStoppedEventArgs args) { - this.EncoderStopped?.Invoke(this, args); + EncoderStopped?.Invoke(this, args); } - + public event EventHandler EncoderFirstStarting; protected virtual void OnEncoderFirstStarting() { diff --git a/ScreenRecorder/Properties/AssemblyInfo.cs b/ScreenRecorder/Properties/AssemblyInfo.cs index c2f6b25..39fbf7f 100644 --- a/ScreenRecorder/Properties/AssemblyInfo.cs +++ b/ScreenRecorder/Properties/AssemblyInfo.cs @@ -50,6 +50,6 @@ // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를 // 기본값으로 할 수 있습니다. // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.4.0")] -[assembly: AssemblyFileVersion("1.1.4.0")] +[assembly: AssemblyVersion("1.1.5.0")] +[assembly: AssemblyFileVersion("1.1.5.0")] [assembly: System.Windows.Media.DisableDpiAwareness] diff --git a/Setup/Setup.vdproj b/Setup/Setup.vdproj index 87b61b4..fa1ca6c 100644 --- a/Setup/Setup.vdproj +++ b/Setup/Setup.vdproj @@ -406,12 +406,6 @@ "Entry" { "MsmKey" = "8:_UNDEFINED" - "OwnerKey" = "8:_1CA83616006F6FF89986A6A496A90EAC" - "MsmSig" = "8:_UNDEFINED" - } - "Entry" - { - "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_D6F63F37F4661857FC2726DBDCC6282C" "MsmSig" = "8:_UNDEFINED" } @@ -466,6 +460,12 @@ "Entry" { "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_1CA83616006F6FF89986A6A496A90EAC" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_1C7CBE96084EDA5D5B405F1164ACCE14" "MsmSig" = "8:_UNDEFINED" } @@ -513,7 +513,7 @@ "DisplayName" = "8:Release" "IsDebugOnly" = "11:FALSE" "IsReleaseOnly" = "11:TRUE" - "OutputFilename" = "8:..\\bin\\x64\\Release_Setup\\ScreenRecorder_Setup_1.1.4.msi" + "OutputFilename" = "8:..\\bin\\x64\\Release_Setup\\ScreenRecorder_Setup_1.1.5.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" @@ -1557,15 +1557,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:ScreenRecorder" - "ProductCode" = "8:{54929989-659D-4E84-B2A2-4FCE0285888F}" - "PackageCode" = "8:{1D3594D8-7D3F-4FAA-8D44-FA8B27192640}" + "ProductCode" = "8:{EF13AAC9-A649-43B9-9215-11278B23B107}" + "PackageCode" = "8:{0634A993-8A1D-46E5-B346-E37697DCE7EE}" "UpgradeCode" = "8:{75B87256-B559-4CF6-ABE6-AEF9F61BD36D}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" - "ProductVersion" = "8:1.1.4" + "ProductVersion" = "8:1.1.5" "Manufacturer" = "8:kimhwan" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:https://github.com/akon47/ScreenRecorder" @@ -2269,7 +2269,7 @@ { "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_3C6494BFE21840E7A935CA4596E91F15" { - "SourcePath" = "8:..\\ScreenRecorder\\obj\\x64\\Release\\ScreenRecorder.exe" + "SourcePath" = "8:" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_7D9EF09E5DF34B809E04523D54E7AEFD"