VideoDecoder.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. using Ryujinx.Graphics.Gpu;
  2. using Ryujinx.Graphics.Gpu.Memory;
  3. using Ryujinx.Graphics.Vic;
  4. using System;
  5. using System.Runtime.InteropServices;
  6. namespace Ryujinx.Graphics.VDec
  7. {
  8. unsafe class VideoDecoder
  9. {
  10. private H264Decoder _h264Decoder;
  11. private Vp9Decoder _vp9Decoder;
  12. private VideoCodec _currentVideoCodec;
  13. private ulong _decoderContextAddress;
  14. private ulong _frameDataAddress;
  15. private ulong _vpxCurrLumaAddress;
  16. private ulong _vpxRef0LumaAddress;
  17. private ulong _vpxRef1LumaAddress;
  18. private ulong _vpxRef2LumaAddress;
  19. private ulong _vpxCurrChromaAddress;
  20. private ulong _vpxRef0ChromaAddress;
  21. private ulong _vpxRef1ChromaAddress;
  22. private ulong _vpxRef2ChromaAddress;
  23. private ulong _vpxProbTablesAddress;
  24. public VideoDecoder()
  25. {
  26. _h264Decoder = new H264Decoder();
  27. _vp9Decoder = new Vp9Decoder();
  28. }
  29. public void Process(GpuContext gpu, int methodOffset, int[] arguments)
  30. {
  31. VideoDecoderMeth method = (VideoDecoderMeth)methodOffset;
  32. switch (method)
  33. {
  34. case VideoDecoderMeth.SetVideoCodec: SetVideoCodec(arguments); break;
  35. case VideoDecoderMeth.Execute: Execute(gpu); break;
  36. case VideoDecoderMeth.SetDecoderCtxAddr: SetDecoderCtxAddr(arguments); break;
  37. case VideoDecoderMeth.SetFrameDataAddr: SetFrameDataAddr(arguments); break;
  38. case VideoDecoderMeth.SetVpxCurrLumaAddr: SetVpxCurrLumaAddr(arguments); break;
  39. case VideoDecoderMeth.SetVpxRef0LumaAddr: SetVpxRef0LumaAddr(arguments); break;
  40. case VideoDecoderMeth.SetVpxRef1LumaAddr: SetVpxRef1LumaAddr(arguments); break;
  41. case VideoDecoderMeth.SetVpxRef2LumaAddr: SetVpxRef2LumaAddr(arguments); break;
  42. case VideoDecoderMeth.SetVpxCurrChromaAddr: SetVpxCurrChromaAddr(arguments); break;
  43. case VideoDecoderMeth.SetVpxRef0ChromaAddr: SetVpxRef0ChromaAddr(arguments); break;
  44. case VideoDecoderMeth.SetVpxRef1ChromaAddr: SetVpxRef1ChromaAddr(arguments); break;
  45. case VideoDecoderMeth.SetVpxRef2ChromaAddr: SetVpxRef2ChromaAddr(arguments); break;
  46. case VideoDecoderMeth.SetVpxProbTablesAddr: SetVpxProbTablesAddr(arguments); break;
  47. }
  48. }
  49. private void SetVideoCodec(int[] arguments)
  50. {
  51. _currentVideoCodec = (VideoCodec)arguments[0];
  52. }
  53. private void Execute(GpuContext gpu)
  54. {
  55. if (_currentVideoCodec == VideoCodec.H264)
  56. {
  57. int frameDataSize = gpu.MemoryAccessor.ReadInt32(_decoderContextAddress + 0x48);
  58. H264ParameterSets Params = gpu.MemoryAccessor.Read<H264ParameterSets>(_decoderContextAddress + 0x58);
  59. H264Matrices matrices = new H264Matrices()
  60. {
  61. ScalingMatrix4 = gpu.MemoryAccessor.ReadBytes(_decoderContextAddress + 0x1c0, 6 * 16),
  62. ScalingMatrix8 = gpu.MemoryAccessor.ReadBytes(_decoderContextAddress + 0x220, 2 * 64)
  63. };
  64. byte[] frameData = gpu.MemoryAccessor.ReadBytes(_frameDataAddress, frameDataSize);
  65. _h264Decoder.Decode(Params, matrices, frameData);
  66. }
  67. else if (_currentVideoCodec == VideoCodec.Vp9)
  68. {
  69. int frameDataSize = gpu.MemoryAccessor.ReadInt32(_decoderContextAddress + 0x30);
  70. Vp9FrameKeys keys = new Vp9FrameKeys()
  71. {
  72. CurrKey = (long)gpu.MemoryManager.Translate(_vpxCurrLumaAddress),
  73. Ref0Key = (long)gpu.MemoryManager.Translate(_vpxRef0LumaAddress),
  74. Ref1Key = (long)gpu.MemoryManager.Translate(_vpxRef1LumaAddress),
  75. Ref2Key = (long)gpu.MemoryManager.Translate(_vpxRef2LumaAddress)
  76. };
  77. Vp9FrameHeader header = ReadStruct<Vp9FrameHeader>(gpu.MemoryAccessor, _decoderContextAddress + 0x48);
  78. Vp9ProbabilityTables probs = new Vp9ProbabilityTables()
  79. {
  80. SegmentationTreeProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x387, 0x7),
  81. SegmentationPredProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x38e, 0x3),
  82. Tx8x8Probs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x470, 0x2),
  83. Tx16x16Probs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x472, 0x4),
  84. Tx32x32Probs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x476, 0x6),
  85. CoefProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x5a0, 0x900),
  86. SkipProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x537, 0x3),
  87. InterModeProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x400, 0x1c),
  88. InterpFilterProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x52a, 0x8),
  89. IsInterProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x41c, 0x4),
  90. CompModeProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x532, 0x5),
  91. SingleRefProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x580, 0xa),
  92. CompRefProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x58a, 0x5),
  93. YModeProbs0 = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x480, 0x20),
  94. YModeProbs1 = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x47c, 0x4),
  95. PartitionProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x4e0, 0x40),
  96. MvJointProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x53b, 0x3),
  97. MvSignProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x53e, 0x3),
  98. MvClassProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x54c, 0x14),
  99. MvClass0BitProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x540, 0x3),
  100. MvBitsProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x56c, 0x14),
  101. MvClass0FrProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x560, 0xc),
  102. MvFrProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x542, 0x6),
  103. MvClass0HpProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x548, 0x2),
  104. MvHpProbs = gpu.MemoryAccessor.ReadBytes(_vpxProbTablesAddress + 0x54a, 0x2)
  105. };
  106. byte[] frameData = gpu.MemoryAccessor.ReadBytes(_frameDataAddress, frameDataSize);
  107. _vp9Decoder.Decode(keys, header, probs, frameData);
  108. }
  109. else
  110. {
  111. ThrowUnimplementedCodec();
  112. }
  113. }
  114. private T ReadStruct<T>(MemoryAccessor accessor, ulong address) where T : struct
  115. {
  116. byte[] data = accessor.ReadBytes(address, Marshal.SizeOf<T>());
  117. unsafe
  118. {
  119. fixed (byte* ptr = data)
  120. {
  121. return Marshal.PtrToStructure<T>((IntPtr)ptr);
  122. }
  123. }
  124. }
  125. private void SetDecoderCtxAddr(int[] arguments)
  126. {
  127. _decoderContextAddress = GetAddress(arguments);
  128. }
  129. private void SetFrameDataAddr(int[] arguments)
  130. {
  131. _frameDataAddress = GetAddress(arguments);
  132. }
  133. private void SetVpxCurrLumaAddr(int[] arguments)
  134. {
  135. _vpxCurrLumaAddress = GetAddress(arguments);
  136. }
  137. private void SetVpxRef0LumaAddr(int[] arguments)
  138. {
  139. _vpxRef0LumaAddress = GetAddress(arguments);
  140. }
  141. private void SetVpxRef1LumaAddr(int[] arguments)
  142. {
  143. _vpxRef1LumaAddress = GetAddress(arguments);
  144. }
  145. private void SetVpxRef2LumaAddr(int[] arguments)
  146. {
  147. _vpxRef2LumaAddress = GetAddress(arguments);
  148. }
  149. private void SetVpxCurrChromaAddr(int[] arguments)
  150. {
  151. _vpxCurrChromaAddress = GetAddress(arguments);
  152. }
  153. private void SetVpxRef0ChromaAddr(int[] arguments)
  154. {
  155. _vpxRef0ChromaAddress = GetAddress(arguments);
  156. }
  157. private void SetVpxRef1ChromaAddr(int[] arguments)
  158. {
  159. _vpxRef1ChromaAddress = GetAddress(arguments);
  160. }
  161. private void SetVpxRef2ChromaAddr(int[] arguments)
  162. {
  163. _vpxRef2ChromaAddress = GetAddress(arguments);
  164. }
  165. private void SetVpxProbTablesAddr(int[] arguments)
  166. {
  167. _vpxProbTablesAddress = GetAddress(arguments);
  168. }
  169. private static ulong GetAddress(int[] arguments)
  170. {
  171. return (ulong)(uint)arguments[0] << 8;
  172. }
  173. internal void CopyPlanes(GpuContext gpu, SurfaceOutputConfig outputConfig)
  174. {
  175. switch (outputConfig.PixelFormat)
  176. {
  177. case SurfacePixelFormat.Rgba8: CopyPlanesRgba8 (gpu, outputConfig); break;
  178. case SurfacePixelFormat.Yuv420P: CopyPlanesYuv420P(gpu, outputConfig); break;
  179. default: ThrowUnimplementedPixelFormat(outputConfig.PixelFormat); break;
  180. }
  181. }
  182. private void CopyPlanesRgba8(GpuContext gpu, SurfaceOutputConfig outputConfig)
  183. {
  184. FFmpegFrame frame = FFmpegWrapper.GetFrameRgba();
  185. if ((frame.Width | frame.Height) == 0)
  186. {
  187. return;
  188. }
  189. throw new NotImplementedException();
  190. }
  191. private void CopyPlanesYuv420P(GpuContext gpu, SurfaceOutputConfig outputConfig)
  192. {
  193. FFmpegFrame frame = FFmpegWrapper.GetFrame();
  194. if ((frame.Width | frame.Height) == 0)
  195. {
  196. return;
  197. }
  198. int halfSrcWidth = frame.Width / 2;
  199. int halfWidth = frame.Width / 2;
  200. int halfHeight = frame.Height / 2;
  201. int alignedWidth = (outputConfig.SurfaceWidth + 0xff) & ~0xff;
  202. for (int y = 0; y < frame.Height; y++)
  203. {
  204. int src = y * frame.Width;
  205. int dst = y * alignedWidth;
  206. int size = frame.Width;
  207. for (int offset = 0; offset < size; offset++)
  208. {
  209. gpu.MemoryAccessor.WriteByte(outputConfig.SurfaceLumaAddress + (ulong)dst + (ulong)offset, *(frame.LumaPtr + src + offset));
  210. }
  211. }
  212. // Copy chroma data from both channels with interleaving.
  213. for (int y = 0; y < halfHeight; y++)
  214. {
  215. int src = y * halfSrcWidth;
  216. int dst = y * alignedWidth;
  217. for (int x = 0; x < halfWidth; x++)
  218. {
  219. gpu.MemoryAccessor.WriteByte(outputConfig.SurfaceChromaUAddress + (ulong)dst + (ulong)x * 2 + 0, *(frame.ChromaBPtr + src + x));
  220. gpu.MemoryAccessor.WriteByte(outputConfig.SurfaceChromaUAddress + (ulong)dst + (ulong)x * 2 + 1, *(frame.ChromaRPtr + src + x));
  221. }
  222. }
  223. }
  224. private void ThrowUnimplementedCodec()
  225. {
  226. throw new NotImplementedException($"Codec \"{_currentVideoCodec}\" is not supported!");
  227. }
  228. private void ThrowUnimplementedPixelFormat(SurfacePixelFormat pixelFormat)
  229. {
  230. throw new NotImplementedException($"Pixel format \"{pixelFormat}\" is not supported!");
  231. }
  232. }
  233. }