From 2f6cecdc2f7c537548408aba91a1c4e18f1f87af Mon Sep 17 00:00:00 2001 From: Sverre Skodje Date: Wed, 7 Sep 2022 00:56:51 +0200 Subject: [PATCH] Added timestamp to new frame callback + added FPS counter to test app. --- ScreenRecorderLib/Callback.h | 4 +- ScreenRecorderLib/Recorder.cpp | 4 +- ScreenRecorderLib/Recorder.h | 4 +- ScreenRecorderLibNative/RecordingManager.cpp | 5 ++- ScreenRecorderLibNative/RecordingManager.h | 2 +- TestApp/App.config | 14 +++--- TestApp/MainWindow.xaml | 23 +++++++--- TestApp/MainWindow.xaml.cs | 47 +++++++++++++++++--- TestApp/Properties/Resources.Designer.cs | 44 ++++++++---------- TestApp/Properties/Settings.Designer.cs | 22 ++++----- TestApp/TestApp.csproj | 3 +- 11 files changed, 104 insertions(+), 68 deletions(-) diff --git a/ScreenRecorderLib/Callback.h b/ScreenRecorderLib/Callback.h index a69e14b..0c5f0de 100644 --- a/ScreenRecorderLib/Callback.h +++ b/ScreenRecorderLib/Callback.h @@ -58,8 +58,10 @@ namespace ScreenRecorderLib { public ref class FrameRecordedEventArgs :System::EventArgs { public: property int FrameNumber; - FrameRecordedEventArgs(int frameNumber) { + property INT64 Timestamp; + FrameRecordedEventArgs(int frameNumber, INT64 timestamp) { FrameNumber = frameNumber; + Timestamp = timestamp; } }; } \ No newline at end of file diff --git a/ScreenRecorderLib/Recorder.cpp b/ScreenRecorderLib/Recorder.cpp index 0da5a95..a89fab2 100644 --- a/ScreenRecorderLib/Recorder.cpp +++ b/ScreenRecorderLib/Recorder.cpp @@ -858,8 +858,8 @@ void ScreenRecorderLib::Recorder::EventSnapshotCreated(std::wstring str) OnSnapshotSaved(this, gcnew SnapshotSavedEventArgs(gcnew String(str.c_str()))); } -void Recorder::FrameNumberChanged(int newFrameNumber) +void Recorder::FrameNumberChanged(int newFrameNumber, INT64 timestamp) { - OnFrameRecorded(this, gcnew FrameRecordedEventArgs(newFrameNumber)); + OnFrameRecorded(this, gcnew FrameRecordedEventArgs(newFrameNumber,timestamp)); CurrentFrameNumber = newFrameNumber; } diff --git a/ScreenRecorderLib/Recorder.h b/ScreenRecorderLib/Recorder.h index 61077d0..a6eb9eb 100644 --- a/ScreenRecorderLib/Recorder.h +++ b/ScreenRecorderLib/Recorder.h @@ -21,7 +21,7 @@ delegate void InternalStatusCallbackDelegate(int status); delegate void InternalCompletionCallbackDelegate(std::wstring path, nlohmann::fifo_map); delegate void InternalErrorCallbackDelegate(std::wstring error, std::wstring path); delegate void InternalSnapshotCallbackDelegate(std::wstring path); -delegate void InternalFrameNumberCallbackDelegate(int newFrameNumber); +delegate void InternalFrameNumberCallbackDelegate(int newFrameNumber, INT64 timestamp); namespace ScreenRecorderLib { @@ -65,7 +65,7 @@ namespace ScreenRecorderLib { void EventFailed(std::wstring error, std::wstring path); void EventStatusChanged(int status); void EventSnapshotCreated(std::wstring str); - void FrameNumberChanged(int newFrameNumber); + void FrameNumberChanged(int newFrameNumber, INT64 timestamp); void SetupCallbacks(); void ClearCallbacks(); static HRESULT CreateNativeRecordingSource(_In_ RecordingSourceBase^ managedSource, _Out_ RECORDING_SOURCE* pNativeSource); diff --git a/ScreenRecorderLibNative/RecordingManager.cpp b/ScreenRecorderLibNative/RecordingManager.cpp index 7ce0898..f15926d 100644 --- a/ScreenRecorderLibNative/RecordingManager.cpp +++ b/ScreenRecorderLibNative/RecordingManager.cpp @@ -313,7 +313,7 @@ void RecordingManager::CleanupDxResources() #if _DEBUG if (m_DxResources.Debug) { m_DxDebugMutex.lock(); - m_DxResources.Debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL| D3D11_RLDO_IGNORE_INTERNAL); + m_DxResources.Debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL | D3D11_RLDO_IGNORE_INTERNAL); m_DxDebugMutex.unlock(); SafeRelease(&m_DxResources.Debug); } @@ -502,7 +502,8 @@ REC_RESULT RecordingManager::StartRecorderLoop(_In_ const std::vectorRenderFrame(model)); frameNr++; if (RecordingFrameNumberChangedCallback != nullptr && !m_IsDestructing) { - RecordingFrameNumberChangedCallback(frameNr); + INT64 timestamp = duration_cast(system_clock::now().time_since_epoch()).count(); + RecordingFrameNumberChangedCallback(frameNr, timestamp); } havePrematureFrame = false; lastFrameStartPos100Nanos += duration100Nanos; diff --git a/ScreenRecorderLibNative/RecordingManager.h b/ScreenRecorderLibNative/RecordingManager.h index 0d0a784..5ec770e 100644 --- a/ScreenRecorderLibNative/RecordingManager.h +++ b/ScreenRecorderLibNative/RecordingManager.h @@ -9,7 +9,7 @@ typedef void(__stdcall *CallbackCompleteFunction)(std::wstring, nlohmann::fifo_m typedef void(__stdcall *CallbackStatusChangedFunction)(int); typedef void(__stdcall *CallbackErrorFunction)(std::wstring, std::wstring); typedef void(__stdcall *CallbackSnapshotFunction)(std::wstring); -typedef void(__stdcall *CallbackFrameNumberChangedFunction)(int); +typedef void(__stdcall *CallbackFrameNumberChangedFunction)(int, INT64); #define STATUS_IDLE 0 #define STATUS_RECORDING 1 diff --git a/TestApp/App.config b/TestApp/App.config index 6daed9e..84cc45d 100644 --- a/TestApp/App.config +++ b/TestApp/App.config @@ -1,18 +1,18 @@ - + - + - - + + - - + + - \ No newline at end of file + diff --git a/TestApp/MainWindow.xaml b/TestApp/MainWindow.xaml index 3c587b2..e7b5f77 100644 --- a/TestApp/MainWindow.xaml +++ b/TestApp/MainWindow.xaml @@ -1207,13 +1207,22 @@ HorizontalAlignment="Center" x:Name="TimeStampTextBlock" Margin="10,0" /> - + + + + + + + + + + + _recordedFrameTimes = new List(); private DateTimeOffset? _recordingStartTime = null; private DateTimeOffset? _recordingPauseTime = null; private Stream _outputStream; @@ -183,6 +184,35 @@ public int CurrentFrameNumber } } + private double _averageFrameRate; + public double AverageFrameRate + { + get { return _averageFrameRate; } + set + { + if (_averageFrameRate != value) + { + _averageFrameRate = value; + RaisePropertyChanged(nameof(AverageFrameRate)); + } + } + } + + private double _currentFrameRate; + public double CurrentFrameRate + { + get { return _currentFrameRate; } + set + { + if (_currentFrameRate != value) + { + _currentFrameRate = value; + RaisePropertyChanged(nameof(CurrentFrameRate)); + } + } + } + + public H264Profile CurrentH264Profile { get; set; } = H264Profile.High; public H265Profile CurrentH265Profile { get; set; } = H265Profile.Main; @@ -212,7 +242,7 @@ private void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs } } } - + private void RecordingSource_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -539,6 +569,7 @@ private void Rec_OnFrameRecorded(object sender, FrameRecordedEventArgs e) Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => { CurrentFrameNumber = e.FrameNumber; + _recordedFrameTimes.Add(e.Timestamp); })); } @@ -696,9 +727,7 @@ private void Rec_OnStatusChanged(object sender, RecordingStatusEventArgs e) case RecorderStatus.Recording: _recordingStartTime = DateTimeOffset.Now; PauseButton.Visibility = Visibility.Visible; - this.FrameNumberTextBlock.Visibility = Visibility.Visible; - if (_progressTimer != null) - _progressTimer.IsEnabled = true; + this.FrameNumberPanel.Visibility = Visibility.Visible; if (_recordingPauseTime != null) { _recordingStartTime = _recordingStartTime.Value.AddTicks((DateTimeOffset.Now.Subtract(_recordingPauseTime.Value)).Ticks); @@ -717,8 +746,7 @@ private void Rec_OnStatusChanged(object sender, RecordingStatusEventArgs e) _progressTimer.Start(); break; case RecorderStatus.Paused: - if (_progressTimer != null) - _progressTimer.IsEnabled = false; + _progressTimer?.Stop(); _recordingPauseTime = DateTimeOffset.Now; PauseButton.Content = "Resume"; this.StatusTextBlock.Text = "Paused"; @@ -736,6 +764,13 @@ private void Rec_OnStatusChanged(object sender, RecordingStatusEventArgs e) private void ProgressTimer_Tick(object sender, EventArgs e) { UpdateProgress(); + if (_recordedFrameTimes.Count > 0) + { + AverageFrameRate = CurrentFrameNumber / DateTimeOffset.FromUnixTimeMilliseconds(_recordedFrameTimes.Last()).Subtract(_recordingStartTime.Value).TotalSeconds; + _recordedFrameTimes.RemoveRange(0, Math.Max(0, _recordedFrameTimes.Count - 10)); + double intervalMillis = (double)(_recordedFrameTimes.Last() - _recordedFrameTimes.First()); + CurrentFrameRate = (_recordedFrameTimes.Count-1) / (double)intervalMillis * 1000; + } } private void UpdateProgress() { diff --git a/TestApp/Properties/Resources.Designer.cs b/TestApp/Properties/Resources.Designer.cs index 08bc180..e3f83c4 100644 --- a/TestApp/Properties/Resources.Designer.cs +++ b/TestApp/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace TestApp.Properties -{ - - +namespace TestApp.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace TestApp.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TestApp.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/TestApp/Properties/Settings.Designer.cs b/TestApp/Properties/Settings.Designer.cs index 361ead0..24def4b 100644 --- a/TestApp/Properties/Settings.Designer.cs +++ b/TestApp/Properties/Settings.Designer.cs @@ -8,21 +8,17 @@ // //------------------------------------------------------------------------------ -namespace TestApp.Properties -{ - - +namespace TestApp.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/TestApp/TestApp.csproj b/TestApp/TestApp.csproj index f76242a..bfc7fc0 100644 --- a/TestApp/TestApp.csproj +++ b/TestApp/TestApp.csproj @@ -9,13 +9,14 @@ Properties TestApp TestApp - v4.5.2 + v4.6 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 true + AnyCPU