ImageEncoder.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /****************************************************************************
  2. * Copyright 2019 Nreal Techonology Limited. All rights reserved.
  3. *
  4. * This file is part of NRSDK.
  5. *
  6. * https://www.nreal.ai/
  7. *
  8. *****************************************************************************/
  9. namespace NRKernal.Record
  10. {
  11. using System.Collections.Generic;
  12. using UnityEngine;
  13. using UnityEngine.Rendering;
  14. /// <summary> An image encoder. </summary>
  15. public class ImageEncoder : IEncoder
  16. {
  17. /// <summary> The current frame. </summary>
  18. RenderTexture m_CurrentFrame;
  19. /// <summary> The requests. </summary>
  20. Queue<AsyncGPUReadbackRequest> m_Requests;
  21. /// <summary> The tasks. </summary>
  22. Queue<CaptureTask> m_Tasks;
  23. /// <summary> Options for controlling the camera. </summary>
  24. CameraParameters m_CameraParameters;
  25. /// <summary> A temp texture for capture. </summary>
  26. Texture2D m_EncodeTempTex = null;
  27. #region Interface
  28. /// <summary> Commits. </summary>
  29. /// <param name="rt"> The right.</param>
  30. /// <param name="timestamp"> The timestamp.</param>
  31. public void Commit(RenderTexture rt, ulong timestamp)
  32. {
  33. m_CurrentFrame = rt;
  34. }
  35. /// <summary> Configurations the given parameter. </summary>
  36. /// <param name="param"> The parameter.</param>
  37. public void Config(CameraParameters param)
  38. {
  39. this.m_CameraParameters = param;
  40. m_Requests = new Queue<AsyncGPUReadbackRequest>();
  41. m_Tasks = new Queue<CaptureTask>();
  42. }
  43. /// <summary> Starts this object. </summary>
  44. public void Start()
  45. {
  46. NRKernalUpdater.OnUpdate -= Update;
  47. NRKernalUpdater.OnUpdate += Update;
  48. }
  49. /// <summary> Stops this object. </summary>
  50. public void Stop()
  51. {
  52. NRKernalUpdater.OnUpdate -= Update;
  53. }
  54. /// <summary> Releases this object. </summary>
  55. public void Release()
  56. {
  57. if (m_EncodeTempTex != null)
  58. {
  59. GameObject.Destroy(m_EncodeTempTex);
  60. m_EncodeTempTex = null;
  61. }
  62. }
  63. #endregion
  64. /// <summary> Commits the given task. </summary>
  65. /// <param name="task"> The task.</param>
  66. public void Commit(CaptureTask task)
  67. {
  68. if (m_CurrentFrame != null)
  69. {
  70. m_Requests.Enqueue(AsyncGPUReadback.Request(m_CurrentFrame));
  71. m_Tasks.Enqueue(task);
  72. }
  73. else
  74. {
  75. NRDebugger.Warning("[ImageEncoder] Lost frame data.");
  76. }
  77. }
  78. /// <summary> Updates this object. </summary>
  79. private void Update()
  80. {
  81. while (m_Requests.Count > 0)
  82. {
  83. var req = m_Requests.Peek();
  84. var task = m_Tasks.Peek();
  85. if (req.hasError)
  86. {
  87. NRDebugger.Info("GPU readback error detected");
  88. m_Requests.Dequeue();
  89. CommitResult(null, task);
  90. m_Tasks.Dequeue();
  91. }
  92. else if (req.done)
  93. {
  94. var buffer = req.GetData<Color32>();
  95. if (m_EncodeTempTex != null &&
  96. m_EncodeTempTex.width != m_CameraParameters.cameraResolutionWidth &&
  97. m_EncodeTempTex.height != m_CameraParameters.cameraResolutionHeight)
  98. {
  99. GameObject.Destroy(m_EncodeTempTex);
  100. m_EncodeTempTex = null;
  101. }
  102. if (m_EncodeTempTex == null)
  103. {
  104. m_EncodeTempTex = new Texture2D(
  105. m_CameraParameters.cameraResolutionWidth,
  106. m_CameraParameters.cameraResolutionHeight,
  107. TextureFormat.RGB24,
  108. false
  109. );
  110. }
  111. m_EncodeTempTex.SetPixels32(buffer.ToArray());
  112. m_EncodeTempTex.Apply();
  113. if (task.OnReceive != null)
  114. {
  115. if (m_EncodeTempTex.width != task.Width || m_EncodeTempTex.height != task.Height)
  116. {
  117. Texture2D scaledtexture;
  118. NRDebugger.Info("[BlendCamera] need to scale the texture which origin width:{0} and out put width:{1}",
  119. m_EncodeTempTex.width, task.Width);
  120. scaledtexture = ImageEncoder.ScaleTexture(m_EncodeTempTex, task.Width, task.Height);
  121. CommitResult(scaledtexture, task);
  122. //Destroy the scale temp texture.
  123. GameObject.Destroy(scaledtexture);
  124. }
  125. else
  126. {
  127. CommitResult(m_EncodeTempTex, task);
  128. }
  129. }
  130. m_Requests.Dequeue();
  131. m_Tasks.Dequeue();
  132. }
  133. else
  134. {
  135. break;
  136. }
  137. }
  138. }
  139. /// <summary> Commits a result. </summary>
  140. /// <param name="texture"> The texture.</param>
  141. /// <param name="task"> The task.</param>
  142. private void CommitResult(Texture2D texture, CaptureTask task)
  143. {
  144. if (task.OnReceive == null)
  145. {
  146. return;
  147. }
  148. if (texture == null)
  149. {
  150. task.OnReceive(task, null);
  151. return;
  152. }
  153. byte[] result = null;
  154. switch (task.CaptureFormat)
  155. {
  156. case PhotoCaptureFileOutputFormat.JPG:
  157. result = texture.EncodeToJPG();
  158. break;
  159. case PhotoCaptureFileOutputFormat.PNG:
  160. result = texture.EncodeToPNG();
  161. break;
  162. default:
  163. break;
  164. }
  165. task.OnReceive(task, result);
  166. }
  167. /// <summary> Encodes. </summary>
  168. /// <param name="width"> The width.</param>
  169. /// <param name="height"> The height.</param>
  170. /// <param name="format"> Describes the format to use.</param>
  171. /// <returns> A byte[]. </returns>
  172. public byte[] Encode(int width, int height, PhotoCaptureFileOutputFormat format)
  173. {
  174. if (m_CurrentFrame == null)
  175. {
  176. NRDebugger.Warning("Current frame is empty!");
  177. return null;
  178. }
  179. byte[] data = null;
  180. RenderTexture pre = RenderTexture.active;
  181. RenderTexture targetRT = m_CurrentFrame;
  182. RenderTexture.active = targetRT;
  183. Texture2D texture2D = new Texture2D(targetRT.width, targetRT.height, TextureFormat.ARGB32, false);
  184. texture2D.ReadPixels(new Rect(0, 0, targetRT.width, targetRT.height), 0, 0);
  185. texture2D.Apply();
  186. RenderTexture.active = pre;
  187. Texture2D outPutTex = texture2D;
  188. Texture2D scaleTexture = null;
  189. // Scale the texture while the output width or height not equal to the targetRT.
  190. if (width != targetRT.width || height != targetRT.height)
  191. {
  192. scaleTexture = ImageEncoder.ScaleTexture(texture2D, width, height);
  193. outPutTex = scaleTexture;
  194. }
  195. switch (format)
  196. {
  197. case PhotoCaptureFileOutputFormat.JPG:
  198. data = outPutTex.EncodeToJPG();
  199. break;
  200. case PhotoCaptureFileOutputFormat.PNG:
  201. data = outPutTex.EncodeToPNG();
  202. break;
  203. default:
  204. break;
  205. }
  206. // Clear the temp texture.
  207. GameObject.Destroy(texture2D);
  208. if (scaleTexture != null)
  209. {
  210. GameObject.Destroy(scaleTexture);
  211. }
  212. return data;
  213. }
  214. /// <summary> Scale texture. </summary>
  215. /// <param name="source"> Source for the.</param>
  216. /// <param name="targetWidth"> Width of the target.</param>
  217. /// <param name="targetHeight"> Height of the target.</param>
  218. /// <returns> A Texture2D. </returns>
  219. public static Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight)
  220. {
  221. NRDebugger.Info("[ImageEncoder] ScaleTexture..");
  222. Texture2D result = new Texture2D(targetWidth, targetHeight, source.format, false);
  223. for (int i = 0; i < result.height; ++i)
  224. {
  225. for (int j = 0; j < result.width; ++j)
  226. {
  227. Color newColor = source.GetPixelBilinear((float)j / (float)result.width, (float)i / (float)result.height);
  228. result.SetPixel(j, i, newColor);
  229. }
  230. }
  231. result.Apply();
  232. return result;
  233. }
  234. }
  235. }