diff --git a/Assets/uNvEncoder/Examples/Scenes/Example.unity b/Assets/uNvEncoder/Examples/Scenes/Example.unity index 489d9f9..72f519a 100644 --- a/Assets/uNvEncoder/Examples/Scenes/Example.unity +++ b/Assets/uNvEncoder/Examples/Scenes/Example.unity @@ -120,6 +120,127 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &512188855 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 512188856} + - component: {fileID: 512188859} + - component: {fileID: 512188858} + - component: {fileID: 512188857} + m_Layer: 0 + m_Name: Render Camera 2 + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &512188856 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 512188855} + m_LocalRotation: {x: 0.31189665, y: 0.20372555, z: 0.068658166, w: 0.92547417} + m_LocalPosition: {x: -1.03, y: 1.4, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 963194228} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 33.321003, y: 30.167002, z: 17.708} +--- !u!114 &512188857 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 512188855} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0833f6be5a296eb4fb05eca6ff7e6439, type: 3} + m_Name: + m_EditorClassIdentifier: + filePath: test-2.h264 +--- !u!114 &512188858 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 512188855} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 28d5e7ad59d1ad44f9e9ea9443db919c, type: 3} + m_Name: + m_EditorClassIdentifier: + encoder: + onEncoded: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 512188857} + m_MethodName: OnEncoded + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: uNvEncoder.Encoder+EncodedCallback, Assembly-CSharp, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null + texture: {fileID: 8400000, guid: fdebf29a4668aa5438593de1aad273f3, type: 2} + frameRate: 60 + forceIdrFrame: 0 +--- !u!20 &512188859 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 512188855} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 8400000, guid: fdebf29a4668aa5438593de1aad273f3, type: 2} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 --- !u!1 &705507993 GameObject: m_ObjectHideFlags: 0 @@ -292,6 +413,7 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1076557919} + - {fileID: 512188856} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -305,11 +427,10 @@ GameObject: m_Component: - component: {fileID: 1076557919} - component: {fileID: 1076557923} - - component: {fileID: 1076557922} - component: {fileID: 1076557921} - component: {fileID: 1076557920} m_Layer: 0 - m_Name: Render Camera + m_Name: Render Camera 1 m_TagString: MainCamera m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -354,38 +475,26 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 28d5e7ad59d1ad44f9e9ea9443db919c, type: 3} m_Name: m_EditorClassIdentifier: - encoder: {fileID: 1076557922} + encoder: + onEncoded: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1076557920} + m_MethodName: OnEncoded + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: uNvEncoder.Encoder+EncodedCallback, Assembly-CSharp, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null texture: {fileID: 8400000, guid: 314a1f1bf10d90f4eb4025543112bd83, type: 2} frameRate: 60 forceIdrFrame: 0 ---- !u!114 &1076557922 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1076557918} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 6ae92fb89500a024088102c416bc5426, type: 3} - m_Name: - m_EditorClassIdentifier: - onEncoded: - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 1076557920} - m_MethodName: OnEncoded - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 - m_TypeName: uNvEncoder.uNvEncoderEncode+EncodedCallback, Assembly-CSharp, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null --- !u!20 &1076557923 Camera: m_ObjectHideFlags: 0 diff --git a/Assets/uNvEncoder/Examples/Scripts/TextureEncoder.cs b/Assets/uNvEncoder/Examples/Scripts/TextureEncoder.cs index 2a673d0..5100fb2 100644 --- a/Assets/uNvEncoder/Examples/Scripts/TextureEncoder.cs +++ b/Assets/uNvEncoder/Examples/Scripts/TextureEncoder.cs @@ -7,23 +7,22 @@ namespace uNvEncoder.Examples public class TextureEncoder : MonoBehaviour { - public uNvEncoder encoder = null; + public Encoder encoder = new Encoder(); public Texture texture = null; public int frameRate = 60; public bool forceIdrFrame = true; void OnEnable() { - Assert.IsNotNull(encoder); Assert.IsNotNull(texture); - encoder.StartEncode(texture.width, texture.height, frameRate); + encoder.Create(texture.width, texture.height, frameRate); StartCoroutine(EncodeLoop()); } void OnDisable() { StopAllCoroutines(); - encoder.StopEncode(); + encoder.Destroy(); } IEnumerator EncodeLoop() @@ -37,14 +36,10 @@ IEnumerator EncodeLoop() void Encode() { - if (!encoder || !encoder.isValid) return; - if (!texture) return; - if (!encoder.Encode(texture, forceIdrFrame)) - { - Debug.LogError("Encode() failed."); - } + encoder.Update(); + encoder.Encode(texture, forceIdrFrame); } } diff --git a/Assets/uNvEncoder/Examples/Textures/Render Camera.renderTexture b/Assets/uNvEncoder/Examples/Textures/Render Camera 1.renderTexture similarity index 96% rename from Assets/uNvEncoder/Examples/Textures/Render Camera.renderTexture rename to Assets/uNvEncoder/Examples/Textures/Render Camera 1.renderTexture index d8b3b2d..ffb5b28 100644 --- a/Assets/uNvEncoder/Examples/Textures/Render Camera.renderTexture +++ b/Assets/uNvEncoder/Examples/Textures/Render Camera 1.renderTexture @@ -6,7 +6,7 @@ RenderTexture: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: Render Camera + m_Name: Render Camera 1 m_ImageContentsHash: serializedVersion: 2 Hash: 00000000000000000000000000000000 diff --git a/Assets/uNvEncoder/Examples/Textures/Render Camera.renderTexture.meta b/Assets/uNvEncoder/Examples/Textures/Render Camera 1.renderTexture.meta similarity index 100% rename from Assets/uNvEncoder/Examples/Textures/Render Camera.renderTexture.meta rename to Assets/uNvEncoder/Examples/Textures/Render Camera 1.renderTexture.meta diff --git a/Assets/uNvEncoder/Examples/Textures/Render Camera 2.renderTexture b/Assets/uNvEncoder/Examples/Textures/Render Camera 2.renderTexture new file mode 100644 index 0000000..ee7a166 --- /dev/null +++ b/Assets/uNvEncoder/Examples/Textures/Render Camera 2.renderTexture @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!84 &8400000 +RenderTexture: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Render Camera 2 + m_ImageContentsHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_ForcedFallbackFormat: 4 + m_DownscaleFallback: 0 + serializedVersion: 3 + m_Width: 1024 + m_Height: 1024 + m_AntiAliasing: 1 + m_DepthFormat: 0 + m_ColorFormat: 8 + m_MipMap: 0 + m_GenerateMips: 1 + m_SRGB: 0 + m_UseDynamicScale: 0 + m_BindMS: 0 + m_EnableCompatibleFormat: 1 + m_TextureSettings: + serializedVersion: 2 + m_FilterMode: 1 + m_Aniso: 0 + m_MipBias: 0 + m_WrapU: 1 + m_WrapV: 1 + m_WrapW: 1 + m_Dimension: 2 + m_VolumeDepth: 1 diff --git a/Assets/uNvEncoder/Examples/Textures/Render Camera 2.renderTexture.meta b/Assets/uNvEncoder/Examples/Textures/Render Camera 2.renderTexture.meta new file mode 100644 index 0000000..3f8c848 --- /dev/null +++ b/Assets/uNvEncoder/Examples/Textures/Render Camera 2.renderTexture.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fdebf29a4668aa5438593de1aad273f3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 8400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uNvEncoder/Plugins/x86/uNvEncoder.dll b/Assets/uNvEncoder/Plugins/x86/uNvEncoder.dll index 008d5b7..fb38339 100644 Binary files a/Assets/uNvEncoder/Plugins/x86/uNvEncoder.dll and b/Assets/uNvEncoder/Plugins/x86/uNvEncoder.dll differ diff --git a/Assets/uNvEncoder/Plugins/x86_64/uNvEncoder.dll b/Assets/uNvEncoder/Plugins/x86_64/uNvEncoder.dll index 6a9bd8e..1e5b6b6 100644 Binary files a/Assets/uNvEncoder/Plugins/x86_64/uNvEncoder.dll and b/Assets/uNvEncoder/Plugins/x86_64/uNvEncoder.dll differ diff --git a/Assets/uNvEncoder/Scripts/uNvEncoder.cs b/Assets/uNvEncoder/Scripts/Encoder.cs similarity index 51% rename from Assets/uNvEncoder/Scripts/uNvEncoder.cs rename to Assets/uNvEncoder/Scripts/Encoder.cs index e8d0a45..f9d762d 100644 --- a/Assets/uNvEncoder/Scripts/uNvEncoder.cs +++ b/Assets/uNvEncoder/Scripts/Encoder.cs @@ -1,82 +1,77 @@ using UnityEngine; using UnityEngine.Events; -using UnityEngine.Assertions; -using System.Threading.Tasks; namespace uNvEncoder { -public class uNvEncoder : MonoBehaviour +[System.Serializable] +public class Encoder { - static uNvEncoder instance; - [System.Serializable] public class EncodedCallback : UnityEvent {}; public EncodedCallback onEncoded = new EncodedCallback(); - Task encodeTask_; + public int id { get; private set; } = -1; public bool isValid { - get { return Lib.IsValid(); } + get { return Lib.IsValid(id); } } public int width { - get { return Lib.GetWidth(); } + get { return Lib.GetWidth(id); } } public int height { - get { return Lib.GetHeight(); } + get { return Lib.GetHeight(id); } } public int frameRate { - get { return Lib.GetFrameRate(); } - } - - void OnDisable() - { + get { return Lib.GetFrameRate(id); } } - void Update() + public string error { - if (!isValid) return; - - Lib.CopyEncodedData(); + get + { + if (!Lib.HasError(id)) return ""; - int n = Lib.GetEncodedDataCount(); - for (int i = 0; i < n; ++i) - { - var size = Lib.GetEncodedDataSize(i); - var data = Lib.GetEncodedDataBuffer(i); - onEncoded.Invoke(data, size); + var str = Lib.GetError(id); + Lib.ClearError(id); + return str; } } - public void StartEncode(int width, int height, int frameRate) + public void Create(int width, int height, int frameRate) { - Assert.IsNull(instance, "Multiple uNvEncoderEncode instance not allowed."); - if (instance) return; - - instance = this; - - Lib.Initialize(width, height, frameRate); + id = Lib.CreateEncoder(width, height, frameRate); if (!isValid) { - Debug.LogError(Lib.GetLastError()); + Debug.LogError(error); } } - public void StopEncode() + public void Destroy() { - Lib.Finalize(); + Lib.DestroyEncoder(id); + } + + public void Update() + { + if (!isValid) return; + + Lib.CopyEncodedData(id); - if (instance == this) + int n = Lib.GetEncodedDataCount(id); + for (int i = 0; i < n; ++i) { - instance = null; + var size = Lib.GetEncodedDataSize(id, i); + var data = Lib.GetEncodedDataBuffer(id, i); + onEncoded.Invoke(data, size); } } @@ -89,7 +84,13 @@ public bool Encode(Texture texture, bool forceIdrFrame) } var ptr = texture.GetNativeTexturePtr(); - return Encode(ptr, forceIdrFrame); + if (!Encode(ptr, forceIdrFrame)) + { + Debug.LogError(error); + return false; + } + + return true; } public bool Encode(System.IntPtr ptr, bool forceIdrFrame) @@ -106,10 +107,10 @@ public bool Encode(System.IntPtr ptr, bool forceIdrFrame) return false; } - var result = Lib.Encode(ptr, forceIdrFrame); + var result = Lib.Encode(id, ptr, forceIdrFrame); if (!result) { - Debug.LogError(Lib.GetLastError()); + Debug.LogError(error); } return result; diff --git a/Assets/uNvEncoder/Scripts/uNvEncoder.cs.meta b/Assets/uNvEncoder/Scripts/Encoder.cs.meta similarity index 100% rename from Assets/uNvEncoder/Scripts/uNvEncoder.cs.meta rename to Assets/uNvEncoder/Scripts/Encoder.cs.meta diff --git a/Assets/uNvEncoder/Scripts/Lib.cs b/Assets/uNvEncoder/Scripts/Lib.cs new file mode 100644 index 0000000..43f5a0b --- /dev/null +++ b/Assets/uNvEncoder/Scripts/Lib.cs @@ -0,0 +1,51 @@ +using System; +using System.Runtime.InteropServices; + +#pragma warning disable CS0465 + +namespace uNvEncoder +{ + +public static class Lib +{ + public const string dllName = "uNvEncoder"; + + // --- + + [DllImport(dllName, EntryPoint = "uNvEncoderCreateEncoder")] + public static extern int CreateEncoder(int width, int height, int frameRate); + [DllImport(dllName, EntryPoint = "uNvEncoderDestroyEncoder")] + public static extern int DestroyEncoder(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderIsValid")] + public static extern bool IsValid(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderGetWidth")] + public static extern int GetWidth(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderGetHeight")] + public static extern int GetHeight(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderGetFrameRate")] + public static extern int GetFrameRate(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderEncode")] + public static extern bool Encode(int id, IntPtr texturePtr, bool forceIdrFrame); + [DllImport(dllName, EntryPoint = "uNvEncoderCopyEncodedData")] + public static extern void CopyEncodedData(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataCount")] + public static extern int GetEncodedDataCount(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataSize")] + public static extern int GetEncodedDataSize(int id, int index); + [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataBuffer")] + public static extern IntPtr GetEncodedDataBuffer(int id, int index); + [DllImport(dllName, EntryPoint = "uNvEncoderGetError")] + private static extern IntPtr GetErrorInternal(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderHasError")] + public static extern bool HasError(int id); + [DllImport(dllName, EntryPoint = "uNvEncoderClearError")] + public static extern void ClearError(int id); + + public static string GetError(int id) + { + var ptr = GetErrorInternal(id); + return Marshal.PtrToStringAnsi(ptr); + } +} + +} diff --git a/Assets/uNvEncoder/Scripts/uNvEncoderLib.cs.meta b/Assets/uNvEncoder/Scripts/Lib.cs.meta similarity index 100% rename from Assets/uNvEncoder/Scripts/uNvEncoderLib.cs.meta rename to Assets/uNvEncoder/Scripts/Lib.cs.meta diff --git a/Assets/uNvEncoder/Scripts/uNvEncoderLib.cs b/Assets/uNvEncoder/Scripts/uNvEncoderLib.cs deleted file mode 100644 index 4ad5e4b..0000000 --- a/Assets/uNvEncoder/Scripts/uNvEncoderLib.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -#pragma warning disable CS0465 - -namespace uNvEncoder -{ - -public static class Lib -{ - public const string dllName = "uNvEncoder"; - - // --- - - [DllImport(dllName, EntryPoint = "uNvEncoderInitialize")] - public static extern void Initialize(int width, int height, int frameRate); - [DllImport(dllName, EntryPoint = "uNvEncoderFinalize")] - public static extern void Finalize(); - [DllImport(dllName, EntryPoint = "uNvEncoderIsValid")] - public static extern bool IsValid(); - [DllImport(dllName, EntryPoint = "uNvEncoderGetWidth")] - public static extern int GetWidth(); - [DllImport(dllName, EntryPoint = "uNvEncoderGetHeight")] - public static extern int GetHeight(); - [DllImport(dllName, EntryPoint = "uNvEncoderGetFrameRate")] - public static extern int GetFrameRate(); - [DllImport(dllName, EntryPoint = "uNvEncoderEncode")] - public static extern bool Encode(IntPtr texturePtr, bool forceIdrFrame); - [DllImport(dllName, EntryPoint = "uNvEncoderCopyEncodedData")] - public static extern void CopyEncodedData(); - [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataCount")] - public static extern int GetEncodedDataCount(); - [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataSize")] - public static extern int GetEncodedDataSize(int i); - [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataBuffer")] - public static extern IntPtr GetEncodedDataBuffer(int i); - [DllImport(dllName, EntryPoint = "uNvEncoderGetEncodedDataIndex")] - public static extern int GetEncodedDataIndex(int i); - [DllImport(dllName, EntryPoint = "uNvEncoderGetLastError")] - private static extern IntPtr GetLastErrorInternal(); - - public static string GetLastError() - { - var ptr = GetLastErrorInternal(); - return Marshal.PtrToStringAnsi(ptr); - } -} - -} diff --git a/Plugins/uNvEncoder/uNvEncoder/Common.cpp b/Plugins/uNvEncoder/uNvEncoder/Common.cpp index efa6139..3106719 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Common.cpp +++ b/Plugins/uNvEncoder/uNvEncoder/Common.cpp @@ -9,7 +9,6 @@ namespace uNvEncoder extern IUnityInterfaces *g_unity; -extern std::string g_error; IUnityInterfaces * GetUnity() @@ -24,10 +23,10 @@ ID3D11Device * GetUnityDevice() } -void DebugError(const std::string &error) +void ThrowError(const std::string &error) { ::OutputDebugStringA((error + "\n").c_str()); - g_error = error; + throw std::exception(error.c_str()); } diff --git a/Plugins/uNvEncoder/uNvEncoder/Common.h b/Plugins/uNvEncoder/uNvEncoder/Common.h index 0c9ad10..08cd2e1 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Common.h +++ b/Plugins/uNvEncoder/uNvEncoder/Common.h @@ -18,7 +18,7 @@ using ComPtr = Microsoft::WRL::ComPtr; struct IUnityInterfaces * GetUnity(); struct ID3D11Device * GetUnityDevice(); -void DebugError(const std::string &error); +void ThrowError(const std::string &error); #define UNVENC_DEBUG_ON diff --git a/Plugins/uNvEncoder/uNvEncoder/Encoder.cpp b/Plugins/uNvEncoder/uNvEncoder/Encoder.cpp index ff9c089..58617aa 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Encoder.cpp +++ b/Plugins/uNvEncoder/uNvEncoder/Encoder.cpp @@ -9,17 +9,31 @@ namespace uNvEncoder Encoder::Encoder(const EncoderDesc &desc) : desc_(desc) { - CreateDevice(); - CreateNvenc(); - StartThread(); + try + { + CreateDevice(); + CreateNvenc(); + StartThread(); + } + catch (const std::exception& e) + { + error_ = e.what(); + } } Encoder::~Encoder() { - StopThread(); - DestroyNvenc(); - DestroyDevice(); + try + { + StopThread(); + DestroyNvenc(); + DestroyDevice(); + } + catch (const std::exception& e) + { + error_ = e.what(); + } } @@ -34,14 +48,14 @@ void Encoder::CreateDevice() ComPtr dxgiDevice; if (FAILED(GetUnityDevice()->QueryInterface(IID_PPV_ARGS(&dxgiDevice)))) { - DebugError("Failed to get IDXGIDevice1."); + ThrowError("Failed to get IDXGIDevice1."); return; } ComPtr dxgiAdapter; if (FAILED(dxgiDevice->GetAdapter(&dxgiAdapter))) { - DebugError("Failed to get IDXGIAdapter."); + ThrowError("Failed to get IDXGIAdapter."); return; } @@ -88,11 +102,13 @@ void Encoder::CreateNvenc() desc.frameRate = desc_.frameRate; nvenc_ = std::make_unique(desc); + nvenc_->Initialize(); } void Encoder::DestroyNvenc() { + nvenc_->Finalize(); nvenc_.reset(); } @@ -124,13 +140,18 @@ void Encoder::StopThread() bool Encoder::Encode(const ComPtr &source, bool forceIdrFrame) { - if (nvenc_->Encode(source, forceIdrFrame)) + try + { + nvenc_->Encode(source, forceIdrFrame); + } + catch (const std::exception& e) { - RequestGetEncodedData(); - return true; + error_ = e.what(); + return false; } - return false; + RequestGetEncodedData(); + return true; } @@ -156,13 +177,21 @@ void Encoder::RequestGetEncodedData() void Encoder::UpdateGetEncodedData() { std::vector data; - if (nvenc_->GetEncodedData(data)) + + try { - std::lock_guard dataLock(encodeDataListMutex_); - for (auto &ed : data) - { - encodedDataList_.push_back(std::move(ed)); - } + nvenc_->GetEncodedData(data); + } + catch (const std::exception& e) + { + error_ = e.what(); + return; + } + + std::lock_guard dataLock(encodeDataListMutex_); + for (auto &ed : data) + { + encodedDataList_.push_back(std::move(ed)); } } diff --git a/Plugins/uNvEncoder/uNvEncoder/Encoder.h b/Plugins/uNvEncoder/uNvEncoder/Encoder.h index 321fd5b..67b7621 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Encoder.h +++ b/Plugins/uNvEncoder/uNvEncoder/Encoder.h @@ -37,6 +37,9 @@ class Encoder final const uint32_t GetWidth() const { return desc_.width; } const uint32_t GetHeight() const { return desc_.height; } const uint32_t GetFrameRate() const { return desc_.frameRate; } + bool HasError() const { return !error_.empty(); } + const std::string & GetError() const { return error_; } + void ClearError() { error_.clear(); } private: void CreateDevice(); @@ -60,6 +63,7 @@ class Encoder final std::mutex encodeDataListMutex_; bool shouldStopEncodeThread_ = false; bool isEncodeRequested = false; + std::string error_; }; diff --git a/Plugins/uNvEncoder/uNvEncoder/Main.cpp b/Plugins/uNvEncoder/uNvEncoder/Main.cpp index b4bedb2..fb8e5b1 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Main.cpp +++ b/Plugins/uNvEncoder/uNvEncoder/Main.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include "Encoder.h" @@ -10,13 +10,19 @@ using namespace uNvEncoder; +using EncoderId = int; namespace uNvEncoder { IUnityInterfaces *g_unity = nullptr; - std::string g_error; - std::unique_ptr g_encoder; +} + + +namespace +{ + std::map> g_encoders; + EncoderId g_encoderId = 0; } @@ -36,117 +42,134 @@ UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload() } -UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API uNvEncoderInitialize(int width, int height, int frameRate) +const std::unique_ptr & GetEncoder(EncoderId id) { - if (g_encoder || !g_unity) return; + static std::unique_ptr invalid; + const auto it = g_encoders.find(id); + return (it != g_encoders.end()) ? it->second : invalid; +} + + +UNITY_INTERFACE_EXPORT EncoderId UNITY_INTERFACE_API uNvEncoderCreateEncoder(int width, int height, int frameRate) +{ + const auto id = g_encoderId++; EncoderDesc desc; desc.width = width; desc.height = height; desc.frameRate = frameRate; - g_encoder = std::make_unique(desc); + auto encoder = std::make_unique(desc); + g_encoders.emplace(id, std::move(encoder)); + + return id; } -UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API uNvEncoderFinalize() +UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API uNvEncoderDestroyEncoder(EncoderId id) { - if (!g_encoder) return; - - g_encoder.reset(); + g_encoders.erase(id); } -UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API uNvEncoderIsValid() +UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API uNvEncoderIsValid(EncoderId id) { - return g_encoder && g_encoder->IsValid(); + const auto &encoder = GetEncoder(id); + return encoder ? encoder->IsValid() : false; } -UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetWidth() +UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetWidth(EncoderId id) { - if (!uNvEncoderIsValid()) return 0; - - return static_cast(g_encoder->GetWidth()); + const auto &encoder = GetEncoder(id); + return encoder ? static_cast(encoder->GetWidth()) : 0; } -UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetHeight() +UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetHeight(EncoderId id) { - if (!uNvEncoderIsValid()) return 0; - - return static_cast(g_encoder->GetHeight()); + const auto &encoder = GetEncoder(id); + return encoder ? static_cast(encoder->GetHeight()) : 0; } -UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetFrameRate() +UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetFrameRate(EncoderId id) { - if (!uNvEncoderIsValid()) return 0; - - return static_cast(g_encoder->GetFrameRate()); + const auto &encoder = GetEncoder(id); + return encoder ? static_cast(encoder->GetFrameRate()) : 0; } -UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API uNvEncoderEncode(ID3D11Texture2D *texture, bool forceIdrFrame) +UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API uNvEncoderEncode(EncoderId id, ID3D11Texture2D *texture, bool forceIdrFrame) { - if (!uNvEncoderIsValid()) return false; - - return g_encoder->Encode(texture, forceIdrFrame); + if (const auto &encoder = GetEncoder(id)) + { + return encoder->Encode(texture, forceIdrFrame); + } + return false; } -UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API uNvEncoderCopyEncodedData() +UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API uNvEncoderCopyEncodedData(EncoderId id) { - if (!uNvEncoderIsValid()) return; - - return g_encoder->CopyEncodedDataList(); + if (const auto &encoder = GetEncoder(id)) + { + encoder->CopyEncodedDataList(); + } } -UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetEncodedDataCount() +UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetEncodedDataCount(EncoderId id) { - if (!uNvEncoderIsValid()) return 0; - - return static_cast(g_encoder->GetEncodedDataList().size()); + const auto &encoder = GetEncoder(id); + return encoder ? static_cast(encoder->GetEncodedDataList().size()) : 0; } -UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetEncodedDataSize(int i) +UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetEncodedDataSize(EncoderId id, int index) { - if (!uNvEncoderIsValid()) return 0; + const auto &encoder = GetEncoder(id); + if (!encoder) return 0; - const auto &list = g_encoder->GetEncodedDataList(); - if (i < 0 || i >= static_cast(list.size())) return 0; + const auto &list = encoder->GetEncodedDataList(); + if (index < 0 || index >= static_cast(list.size())) return 0; - return static_cast(list.at(i).size); + return static_cast(list.at(index).size); } -UNITY_INTERFACE_EXPORT const void * UNITY_INTERFACE_API uNvEncoderGetEncodedDataBuffer(int i) +UNITY_INTERFACE_EXPORT const void * UNITY_INTERFACE_API uNvEncoderGetEncodedDataBuffer(EncoderId id, int index) { - if (!uNvEncoderIsValid()) return nullptr; + const auto &encoder = GetEncoder(id); + if (!encoder) return nullptr; - const auto &list = g_encoder->GetEncodedDataList(); - if (i < 0 || i >= static_cast(list.size())) return nullptr; + const auto &list = encoder->GetEncodedDataList(); + if (index < 0 || index >= static_cast(list.size())) return nullptr; - return list.at(i).buffer.get(); + return list.at(index).buffer.get(); } -UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API uNvEncoderGetEncodedDataIndex(int i) +UNITY_INTERFACE_EXPORT const char * UNITY_INTERFACE_API uNvEncoderGetError(EncoderId id) { - if (!uNvEncoderIsValid()) return -1; + const auto &encoder = GetEncoder(id); + return encoder ? encoder->GetError().c_str() : nullptr; +} - const auto &list = g_encoder->GetEncodedDataList(); - if (i < 0 || i >= static_cast(list.size())) return -1; - return static_cast(list.at(i).index); +UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API uNvEncoderHasError(EncoderId id) +{ + const auto &encoder = GetEncoder(id); + return encoder ? encoder->HasError() : false; } -UNITY_INTERFACE_EXPORT const char * UNITY_INTERFACE_API uNvEncoderGetLastError() +UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API uNvEncoderClearError(EncoderId id) { - return g_error.c_str(); + if (const auto &encoder = GetEncoder(id)) + { + encoder->ClearError(); + } } diff --git a/Plugins/uNvEncoder/uNvEncoder/Nvenc.cpp b/Plugins/uNvEncoder/uNvEncoder/Nvenc.cpp index 7eac77a..b3d6fcd 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Nvenc.cpp +++ b/Plugins/uNvEncoder/uNvEncoder/Nvenc.cpp @@ -42,7 +42,7 @@ void OutputNvencApiError(const std::string &apiName, NVENCSTATUS status) const auto it = nvEncStatusErrorNameTable.find(status); const auto statusStr = it != nvEncStatusErrorNameTable.end() ? it->second : "Unknown"; - DebugError(apiName + " call failed: " + statusStr); + ThrowError(apiName + " call failed: " + statusStr); } @@ -62,15 +62,79 @@ NVENCSTATUS CallNvencApi(const std::string &apiName, const Api &api, const Args #define CALL_NVENC_API(Api, ...) CallNvencApi(#Api, Api, __VA_ARGS__) + +decltype(Nvenc::s_module) Nvenc::s_module = NULL; +decltype(Nvenc::s_nvenc) Nvenc::s_nvenc = { 0 }; +decltype(Nvenc::s_referenceCount) Nvenc::s_referenceCount = 0; + + +void Nvenc::LoadModule() +{ + ++s_referenceCount; + + if (s_module != NULL) return; + +#if defined(_WIN64) + s_module = ::LoadLibraryA("nvEncodeAPI64.dll"); +#else + s_module = ::LoadLibraryA("nvEncodeAPI.dll"); +#endif + if (s_module == NULL) ThrowError("NVENC is not available."); + + if (const auto funcAddress = ::GetProcAddress(s_module, "NvEncodeAPIGetMaxSupportedVersion")) + { + using FuncType = decltype(NvEncodeAPIGetMaxSupportedVersion); + const auto func = reinterpret_cast(funcAddress); + uint32_t version = 0; + const auto res = func(&version); + constexpr uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + if (currentVersion > version) ThrowError("NVENC version is wrong."); + } + + if (const auto funcAddress = ::GetProcAddress(s_module, "NvEncodeAPICreateInstance")) + { + using FuncType = decltype(NvEncodeAPICreateInstance); + const auto func = reinterpret_cast(funcAddress); + s_nvenc = { NV_ENCODE_API_FUNCTION_LIST_VER }; + func(&s_nvenc); + + if (!s_nvenc.nvEncOpenEncodeSession) + { + ThrowError("Failed to load functions from DLL."); + } + } +} + + +void Nvenc::UnloadModule() +{ + if (--s_referenceCount > 0) return; + + if (s_module != NULL) + { + ::FreeLibrary(s_module); + s_module = NULL; + } +} + + Nvenc::Nvenc(const NvencDesc &desc) : desc_(desc) , resources_(1) { - if (!LoadModule()) - { - return; - } +} + +Nvenc::~Nvenc() +{ +} + + +void Nvenc::Initialize() +{ + if (isInitialized_) return; + + LoadModule(); OpenEncodeSession(); InitializeEncoder(); @@ -78,61 +142,29 @@ Nvenc::Nvenc(const NvencDesc &desc) CreateInputTextures(); RegisterResources(); CreateBitstreamBuffers(); + + isInitialized_ = true; } -Nvenc::~Nvenc() +void Nvenc::Finalize() { + if (!isInitialized_) return; + EndEncode(); DestroyBitstreamBuffers(); UnregisterResources(); DestroyCompletionEvents(); DestroyEncoder(); - UnloadModule(); -} - - -bool Nvenc::LoadModule() -{ -#if defined(_WIN64) - module_ = ::LoadLibraryA("nvEncodeAPI64.dll"); -#else - module_ = ::LoadLibraryA("nvEncodeAPI.dll"); -#endif - if (!module_) return false; -#define CALL_NVENC_API_FROM_DLL(API, ...) \ - using API##Func = decltype(API); \ - const auto API##Address = GetProcAddress(module_, #API); \ - if (!API##Address) return false; \ - const auto API = reinterpret_cast(API##Address); \ - const auto API##Result = API(__VA_ARGS__); \ - if (API##Result != NV_ENC_SUCCESS) return false; - - uint32_t version = 0; - CALL_NVENC_API_FROM_DLL(NvEncodeAPIGetMaxSupportedVersion, &version); - const uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; - if (currentVersion > version) return false; - - nvenc_ = { NV_ENCODE_API_FUNCTION_LIST_VER }; - CALL_NVENC_API_FROM_DLL(NvEncodeAPICreateInstance, &nvenc_); - - if (!nvenc_.nvEncOpenEncodeSession) - { - return false; - } - - return true; + isInitialized_ = false; } -void Nvenc::UnloadModule() +void Nvenc::ThrowErrorIfNotInitialized() { - if (!module_) return; - - ::FreeLibrary(module_); - module_ = nullptr; + if (!IsValid()) ThrowError("NVENC has not been initialized yet."); } @@ -142,7 +174,7 @@ void Nvenc::OpenEncodeSession() encSessionParams.device = desc_.d3d11Device.Get(); encSessionParams.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; encSessionParams.apiVersion = NVENCAPI_VERSION; - CALL_NVENC_API(nvenc_.nvEncOpenEncodeSessionEx, &encSessionParams, &encoder_); + CALL_NVENC_API(s_nvenc.nvEncOpenEncodeSessionEx, &encSessionParams, &encoder_); } @@ -167,7 +199,7 @@ void Nvenc::InitializeEncoder() initParams.enableEncodeAsync = true; NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } }; - CALL_NVENC_API(nvenc_.nvEncGetEncodePresetConfig, encoder_, initParams.encodeGUID, initParams.presetGUID, &presetConfig); + CALL_NVENC_API(s_nvenc.nvEncGetEncodePresetConfig, encoder_, initParams.encodeGUID, initParams.presetGUID, &presetConfig); NV_ENC_CONFIG config = { NV_ENC_CONFIG_VER }; memcpy(&config, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); @@ -181,27 +213,27 @@ void Nvenc::InitializeEncoder() config.encodeCodecConfig.h264Config.maxNumRefFrames = 0; config.encodeCodecConfig.h264Config.idrPeriod = config.gopLength; - CALL_NVENC_API(nvenc_.nvEncInitializeEncoder, encoder_, &initParams); + CALL_NVENC_API(s_nvenc.nvEncInitializeEncoder, encoder_, &initParams); } void Nvenc::CreateCompletionEvents() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); for (auto &resource : resources_) { resource.completionEvent_ = ::CreateEventA(NULL, FALSE, FALSE, NULL); NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER }; eventParams.completionEvent = resource.completionEvent_; - CALL_NVENC_API(nvenc_.nvEncRegisterAsyncEvent, encoder_, &eventParams); + CALL_NVENC_API(s_nvenc.nvEncRegisterAsyncEvent, encoder_, &eventParams); } } void Nvenc::DestroyCompletionEvents() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); for (auto &resource : resources_) { @@ -209,7 +241,7 @@ void Nvenc::DestroyCompletionEvents() NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER }; eventParams.completionEvent = resource.completionEvent_; - CALL_NVENC_API(nvenc_.nvEncUnregisterAsyncEvent, encoder_, &eventParams); + CALL_NVENC_API(s_nvenc.nvEncUnregisterAsyncEvent, encoder_, &eventParams); ::CloseHandle(resource.completionEvent_); } } @@ -217,12 +249,12 @@ void Nvenc::DestroyCompletionEvents() void Nvenc::CreateBitstreamBuffers() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); for (auto &resource : resources_) { NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER }; - CALL_NVENC_API(nvenc_.nvEncCreateBitstreamBuffer, encoder_, &createBitstreamBuffer); + CALL_NVENC_API(s_nvenc.nvEncCreateBitstreamBuffer, encoder_, &createBitstreamBuffer); resource.bitstreamBuffer_ = createBitstreamBuffer.bitstreamBuffer; } } @@ -230,7 +262,7 @@ void Nvenc::CreateBitstreamBuffers() void Nvenc::CreateInputTextures() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); D3D11_TEXTURE2D_DESC desc = { 0 }; desc.Width = desc_.width; @@ -248,7 +280,7 @@ void Nvenc::CreateInputTextures() { if (FAILED(desc_.d3d11Device->CreateTexture2D(&desc, NULL, &resource.inputTexture_))) { - DebugError("Failed to create shared texture."); + ThrowError("Failed to create shared texture."); return; } @@ -256,7 +288,7 @@ void Nvenc::CreateInputTextures() resource.inputTexture_.As(&dxgiResource); if (FAILED(dxgiResource->GetSharedHandle(&resource.inputTextureSharedHandle_))) { - DebugError("Failed to get shared handle."); + ThrowError("Failed to get shared handle."); return; } } @@ -265,7 +297,7 @@ void Nvenc::CreateInputTextures() void Nvenc::RegisterResources() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); for (auto &resource : resources_) { @@ -277,7 +309,7 @@ void Nvenc::RegisterResources() registerResource.pitch = 0; registerResource.bufferFormat = NV_ENC_BUFFER_FORMAT_ARGB; registerResource.bufferUsage = NV_ENC_INPUT_IMAGE; - CALL_NVENC_API(nvenc_.nvEncRegisterResource, encoder_, ®isterResource); + CALL_NVENC_API(s_nvenc.nvEncRegisterResource, encoder_, ®isterResource); resource.registeredResource_ = registerResource.registeredResource; } @@ -286,47 +318,46 @@ void Nvenc::RegisterResources() void Nvenc::UnregisterResources() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); for (auto &resource : resources_) { if (!resource.registeredResource_) continue; - CALL_NVENC_API(nvenc_.nvEncUnregisterResource, encoder_, resource.registeredResource_); + CALL_NVENC_API(s_nvenc.nvEncUnregisterResource, encoder_, resource.registeredResource_); } } void Nvenc::DestroyBitstreamBuffers() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); for (auto &resource : resources_) { if (!resource.bitstreamBuffer_) continue; - CALL_NVENC_API(nvenc_.nvEncDestroyBitstreamBuffer, encoder_, resource.bitstreamBuffer_); + CALL_NVENC_API(s_nvenc.nvEncDestroyBitstreamBuffer, encoder_, resource.bitstreamBuffer_); } } void Nvenc::DestroyEncoder() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); - CALL_NVENC_API(nvenc_.nvEncDestroyEncoder, encoder_); + CALL_NVENC_API(s_nvenc.nvEncDestroyEncoder, encoder_); } -bool Nvenc::Encode(const ComPtr &source, bool forceIdrFrame) +void Nvenc::Encode(const ComPtr &source, bool forceIdrFrame) { - if (!IsValid()) return false; + ThrowErrorIfNotInitialized(); const auto index = GetInputIndex(); auto &resource = resources_[index]; if (resource.isEncoding_) { - DebugError("The previous encode is still continuing."); - return false; + ThrowError("The previous encode is still continuing."); } resource.isEncoding_ = true; @@ -336,19 +367,17 @@ bool Nvenc::Encode(const ComPtr &source, bool forceIdrFrame) if (EncodeInputTexture(index, forceIdrFrame)) { ++inputIndex_; - return true; } else { resource.isEncoding_ = false; - return false; } } void Nvenc::CopyToInputTexture(int index, const ComPtr &texture) { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); auto &resource = resources_[index]; ComPtr inputTexture; @@ -358,7 +387,7 @@ void Nvenc::CopyToInputTexture(int index, const ComPtr &texture __uuidof(ID3D11Texture2D), &inputTexture))) { - DebugError("Failed to open shared texture from shared handle."); + ThrowError("Failed to open shared texture from shared handle."); return; } @@ -371,7 +400,7 @@ void Nvenc::CopyToInputTexture(int index, const ComPtr &texture bool Nvenc::EncodeInputTexture(int index, bool forceIdrFrame) { - if (!IsValid()) return false; + ThrowErrorIfNotInitialized(); auto &resource = resources_[index]; @@ -389,7 +418,7 @@ bool Nvenc::EncodeInputTexture(int index, bool forceIdrFrame) picParams.encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR | NV_ENC_PIC_FLAG_OUTPUT_SPSPPS; } - const auto status = CALL_NVENC_API(nvenc_.nvEncEncodePicture, encoder_, &picParams); + const auto status = CALL_NVENC_API(s_nvenc.nvEncEncodePicture, encoder_, &picParams); if (status != NV_ENC_SUCCESS && status != NV_ENC_ERR_NEED_MORE_INPUT) { return false; @@ -401,32 +430,35 @@ bool Nvenc::EncodeInputTexture(int index, bool forceIdrFrame) void Nvenc::MapInputResource(int index) { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); auto &resource = resources_[index]; if (!resource.registeredResource_) return; NV_ENC_MAP_INPUT_RESOURCE mapInputResource = { NV_ENC_MAP_INPUT_RESOURCE_VER }; mapInputResource.registeredResource = resource.registeredResource_; - CALL_NVENC_API(nvenc_.nvEncMapInputResource, encoder_, &mapInputResource); + CALL_NVENC_API(s_nvenc.nvEncMapInputResource, encoder_, &mapInputResource); resource.inputResource_ = mapInputResource.mappedResource; } void Nvenc::UnmapInputResource(int index) { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); auto &resource = resources_[index]; - CALL_NVENC_API(nvenc_.nvEncUnmapInputResource, encoder_, resource.inputResource_); - resource.inputResource_ = nullptr; + if (!resource.inputResource_) + { + CALL_NVENC_API(s_nvenc.nvEncUnmapInputResource, encoder_, resource.inputResource_); + resource.inputResource_ = nullptr; + } } -bool Nvenc::GetEncodedData(std::vector &data) +void Nvenc::GetEncodedData(std::vector &data) { - if (!IsValid()) return false; + ThrowErrorIfNotInitialized(); for (;outputIndex_ < inputIndex_; ++outputIndex_) { @@ -435,21 +467,21 @@ bool Nvenc::GetEncodedData(std::vector &data) if (!resource.isEncoding_) { - DebugError("Try to get an invalid bitstream."); + ThrowError("Try to get an invalid bitstream."); continue; } constexpr DWORD duration = 10000; if (!WaitForCompletion(index, duration)) { - DebugError("Timeout when getting an encoded bitstream."); + ThrowError("Timeout when getting an encoded bitstream."); continue; } NV_ENC_LOCK_BITSTREAM lockBitstream = { NV_ENC_LOCK_BITSTREAM_VER }; lockBitstream.outputBitstream = resource.bitstreamBuffer_; lockBitstream.doNotWait = false; - CALL_NVENC_API(nvenc_.nvEncLockBitstream, encoder_, &lockBitstream); + CALL_NVENC_API(s_nvenc.nvEncLockBitstream, encoder_, &lockBitstream); NvencEncodedData ed; ed.index = outputIndex_; @@ -458,26 +490,24 @@ bool Nvenc::GetEncodedData(std::vector &data) ::memcpy(ed.buffer.get(), lockBitstream.bitstreamBufferPtr, ed.size); data.push_back(std::move(ed)); - CALL_NVENC_API(nvenc_.nvEncUnlockBitstream, encoder_, resource.bitstreamBuffer_); + CALL_NVENC_API(s_nvenc.nvEncUnlockBitstream, encoder_, resource.bitstreamBuffer_); UnmapInputResource(index); resource.isEncoding_ = false; } - - return !data.empty(); } bool Nvenc::WaitForCompletion(int index, DWORD duration) { - if (!IsValid()) return false; + ThrowErrorIfNotInitialized(); auto &resource = resources_[index]; if (::WaitForSingleObject(resource.completionEvent_, duration) == WAIT_FAILED) { - DebugError("Failed to wait for encode completion."); + ThrowError("Failed to wait for encode completion."); return false; } @@ -487,7 +517,9 @@ bool Nvenc::WaitForCompletion(int index, DWORD duration) void Nvenc::EndEncode() { - if (!IsValid() || inputIndex_ == 0U) return; + ThrowErrorIfNotInitialized(); + + if (inputIndex_ == 0U) return; SendEOS(); @@ -498,14 +530,15 @@ void Nvenc::EndEncode() void Nvenc::SendEOS() { - if (!IsValid()) return; + ThrowErrorIfNotInitialized(); auto &resource = resources_[GetInputIndex()]; + resource.isEncoding_ = true; NV_ENC_PIC_PARAMS picParams = { NV_ENC_PIC_PARAMS_VER }; picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS; picParams.completionEvent = resource.completionEvent_; - CALL_NVENC_API(nvenc_.nvEncEncodePicture, encoder_, &picParams); + CALL_NVENC_API(s_nvenc.nvEncEncodePicture, encoder_, &picParams); ++inputIndex_; } diff --git a/Plugins/uNvEncoder/uNvEncoder/Nvenc.h b/Plugins/uNvEncoder/uNvEncoder/Nvenc.h index 1531c8d..f352059 100644 --- a/Plugins/uNvEncoder/uNvEncoder/Nvenc.h +++ b/Plugins/uNvEncoder/uNvEncoder/Nvenc.h @@ -35,16 +35,17 @@ class Nvenc final public: explicit Nvenc(const NvencDesc &desc); ~Nvenc(); + void Initialize(); + void Finalize(); bool IsValid() const { return encoder_ != nullptr; } - bool Encode(const ComPtr &source, bool forceIdrFrame); - bool GetEncodedData(std::vector &data); + void Encode(const ComPtr &source, bool forceIdrFrame); + void GetEncodedData(std::vector &data); const uint32_t GetWidth() const { return desc_.width; } const uint32_t GetHeight() const { return desc_.height; } const uint32_t GetFrameRate() const { return desc_.frameRate; } private: - bool LoadModule(); - void UnloadModule(); + void ThrowErrorIfNotInitialized(); void OpenEncodeSession(); void InitializeEncoder(); @@ -70,8 +71,7 @@ class Nvenc final unsigned long GetOutputIndex() const { return outputIndex_ % GetResourceCount(); } const NvencDesc desc_; - HMODULE module_ = nullptr; - NV_ENCODE_API_FUNCTION_LIST nvenc_ = { 0 }; + bool isInitialized_ = false; void *encoder_ = nullptr; uint64_t inputIndex_ = 0U; uint64_t outputIndex_ = 0U; @@ -87,6 +87,15 @@ class Nvenc final std::atomic isEncoding_ = false; }; std::vector resources_; + +public: + static void LoadModule(); + static void UnloadModule(); + +private: + static HMODULE s_module; + static NV_ENCODE_API_FUNCTION_LIST s_nvenc; + static uint32_t s_referenceCount; };