PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/OSU player/OSU player/Graphic/VideoDecoder.cs

https://github.com/1109616856/OSUplayer
C# | 284 lines | 284 code | 0 blank | 0 comment | 39 complexity | 592f97be1956b69fd4abd878d8a8b0ea MD5 | raw file
Possible License(s): GPL-3.0
  1. namespace OSUplayer.Graphic
  2. {
  3. using System;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using System.Threading;
  7. public class VideoDecoder : IDisposable
  8. {
  9. private IntPtr buffer;
  10. public int BufferSize;
  11. private FFmpeg.AVCodecContext codecCtx;
  12. private double currentDisplayTime;
  13. private Thread decodingThread;
  14. private FFmpeg.AVFormatContext formatContext;
  15. private byte[][] FrameBuffer;
  16. private double[] FrameBufferTimes;
  17. public double FrameDelay;
  18. private int frameFinished;
  19. private bool isDisposed;
  20. private double lastPts;
  21. private IntPtr packet;
  22. private IntPtr pCodec;
  23. private IntPtr pCodecCtx;
  24. private IntPtr pFormatCtx;
  25. private IntPtr pFrame;
  26. private IntPtr pFrameRGB;
  27. private const int PIXEL_FORMAT = 6;
  28. private int readCursor;
  29. private FFmpeg.AVStream stream;
  30. private GCHandle streamHandle;
  31. private bool videoOpened;
  32. private int videoStream;
  33. private int writeCursor;
  34. public VideoDecoder(int bufferSize)
  35. {
  36. this.BufferSize = bufferSize;
  37. this.FrameBufferTimes = new double[this.BufferSize];
  38. this.FrameBuffer = new byte[this.BufferSize][];
  39. FFmpeg.av_register_all();
  40. this.videoOpened = false;
  41. }
  42. private void Decode()
  43. {
  44. try
  45. {
  46. while (true)
  47. {
  48. bool flag = false;
  49. lock (this)
  50. {
  51. while (((this.writeCursor - this.readCursor) < this.BufferSize) && (FFmpeg.av_read_frame(this.pFormatCtx, this.packet) >= 0))
  52. {
  53. if (Marshal.ReadInt32(this.packet, 0x18) == this.videoStream)
  54. {
  55. double num = Marshal.ReadInt64(this.packet, 8);
  56. IntPtr buf = Marshal.ReadIntPtr(this.packet, 0x10);
  57. int num2 = Marshal.ReadInt32(this.packet, 20);
  58. FFmpeg.avcodec_decode_video(this.pCodecCtx, this.pFrame, ref this.frameFinished, buf, num2);
  59. if (((this.frameFinished != 0) && (Marshal.ReadIntPtr(this.packet, 0x10) != IntPtr.Zero)) && (FFmpeg.img_convert(this.pFrameRGB, 6, this.pFrame, (int)this.codecCtx.pix_fmt, this.codecCtx.width, this.codecCtx.height) == 0))
  60. {
  61. Marshal.Copy(Marshal.ReadIntPtr(this.pFrameRGB), this.FrameBuffer[this.writeCursor % this.BufferSize], 0, this.FrameBuffer[this.writeCursor % this.BufferSize].Length);
  62. this.FrameBufferTimes[this.writeCursor % this.BufferSize] = ((num - this.stream.start_time) * this.FrameDelay) * 1000.0;
  63. this.writeCursor++;
  64. this.lastPts = num;
  65. flag = true;
  66. }
  67. }
  68. }
  69. }
  70. if (!flag)
  71. {
  72. Thread.Sleep(15);
  73. }
  74. }
  75. }
  76. catch (ThreadAbortException)
  77. {
  78. }
  79. catch (Exception exception)
  80. {
  81. using (StreamWriter writer = File.CreateText("video-debug.txt"))
  82. {
  83. writer.WriteLine(exception.ToString());
  84. }
  85. }
  86. }
  87. public void Dispose()
  88. {
  89. this.Dispose(true);
  90. }
  91. public void Dispose(bool disposing)
  92. {
  93. if (!this.isDisposed)
  94. {
  95. this.isDisposed = true;
  96. if (this.decodingThread != null)
  97. {
  98. this.decodingThread.Abort();
  99. }
  100. try
  101. {
  102. Marshal.FreeHGlobal(this.packet);
  103. Marshal.FreeHGlobal(this.buffer);
  104. }
  105. catch { }
  106. try
  107. {
  108. FFmpeg.av_free(this.pFrameRGB);
  109. FFmpeg.av_free(this.pFrame);
  110. }
  111. catch { }
  112. try
  113. {
  114. this.streamHandle.Free();
  115. }
  116. catch { }
  117. this.frameFinished = 0;
  118. try
  119. {
  120. FFmpeg.avcodec_close(this.pCodecCtx);
  121. FFmpeg.av_close_input_file(this.pFormatCtx);
  122. }
  123. catch { }
  124. }
  125. }
  126. ~VideoDecoder()
  127. {
  128. this.Dispose(false);
  129. }
  130. public byte[] GetFrame(int time)
  131. {
  132. while ((this.readCursor < (this.writeCursor - 1)) && (this.FrameBufferTimes[(this.readCursor + 1) % this.BufferSize] <= time))
  133. {
  134. this.readCursor++;
  135. }
  136. if (this.readCursor < this.writeCursor)
  137. {
  138. this.currentDisplayTime = this.FrameBufferTimes[this.readCursor % this.BufferSize];
  139. return this.FrameBuffer[this.readCursor % this.BufferSize];
  140. }
  141. return null;
  142. }
  143. public bool Open(string path)
  144. {
  145. FileStream inStream = File.OpenRead(path);
  146. this.OpenStream(inStream);
  147. return true;
  148. }
  149. public bool Open(byte[] bytes)
  150. {
  151. if ((bytes == null) || (bytes.Length == 0))
  152. {
  153. return false;
  154. }
  155. this.streamHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  156. IntPtr ptr = this.streamHandle.AddrOfPinnedObject();
  157. if (this.videoOpened)
  158. {
  159. return false;
  160. }
  161. this.videoOpened = true;
  162. string filename = string.Concat(new object[] { "memory:", ptr, "|", bytes.Length });
  163. if (FFmpeg.av_open_input_file(out this.pFormatCtx, filename, IntPtr.Zero, bytes.Length, IntPtr.Zero) != 0)
  164. {
  165. throw new Exception("Couldn't open input file");
  166. }
  167. if (FFmpeg.av_find_stream_info(this.pFormatCtx) < 0)
  168. {
  169. throw new Exception("Couldn't find stream info");
  170. }
  171. FFmpeg.dump_format(this.pFormatCtx, 0, filename, 0);
  172. this.formatContext = (FFmpeg.AVFormatContext)Marshal.PtrToStructure(this.pFormatCtx, typeof(FFmpeg.AVFormatContext));
  173. this.videoStream = -1;
  174. int num2 = this.formatContext.nb_streams;
  175. for (int i = 0; i < num2; i++)
  176. {
  177. FFmpeg.AVStream stream = (FFmpeg.AVStream)Marshal.PtrToStructure(this.formatContext.streams[i], typeof(FFmpeg.AVStream));
  178. FFmpeg.AVCodecContext context = (FFmpeg.AVCodecContext)Marshal.PtrToStructure(stream.codec, typeof(FFmpeg.AVCodecContext));
  179. if (context.codec_type == FFmpeg.CodecType.CODEC_TYPE_VIDEO)
  180. {
  181. this.videoStream = i;
  182. this.stream = stream;
  183. this.codecCtx = context;
  184. this.pCodecCtx = this.stream.codec;
  185. break;
  186. }
  187. }
  188. if (this.videoStream == -1)
  189. {
  190. throw new Exception("couldn't find video stream");
  191. }
  192. this.FrameDelay = FFmpeg.av_q2d(this.stream.time_base);
  193. this.pCodec = FFmpeg.avcodec_find_decoder(this.codecCtx.codec_id);
  194. if (this.pCodec == IntPtr.Zero)
  195. {
  196. throw new Exception("couldn't find decoder");
  197. }
  198. if (FFmpeg.avcodec_open(this.pCodecCtx, this.pCodec) < 0)
  199. {
  200. throw new Exception("couldn't open codec");
  201. }
  202. this.pFrame = FFmpeg.avcodec_alloc_frame();
  203. this.pFrameRGB = FFmpeg.avcodec_alloc_frame();
  204. if (this.pFrameRGB == IntPtr.Zero)
  205. {
  206. throw new Exception("couldn't allocate RGB frame");
  207. }
  208. int cb = FFmpeg.avpicture_get_size(6, this.codecCtx.width, this.codecCtx.height);
  209. this.buffer = Marshal.AllocHGlobal(cb);
  210. FFmpeg.avpicture_fill(this.pFrameRGB, this.buffer, 6, this.codecCtx.width, this.codecCtx.height);
  211. this.packet = Marshal.AllocHGlobal(0x39);
  212. for (int j = 0; j < this.BufferSize; j++)
  213. {
  214. this.FrameBuffer[j] = new byte[(this.width * this.height) * 4];
  215. }
  216. this.decodingThread = new Thread(new ThreadStart(this.Decode));
  217. this.decodingThread.IsBackground = true;
  218. this.decodingThread.Start();
  219. return true;
  220. }
  221. public bool OpenStream(Stream inStream)
  222. {
  223. byte[] buffer = new byte[inStream.Length];
  224. inStream.Read(buffer, 0, (int)inStream.Length);
  225. return this.Open(buffer);
  226. }
  227. public void Seek(int time)
  228. {
  229. lock (this)
  230. {
  231. int flags = 0;
  232. double num2 = ((((double)time) / 1000.0) / this.FrameDelay) + this.stream.start_time;
  233. if (num2 < this.lastPts)
  234. {
  235. flags = 1;
  236. }
  237. FFmpeg.av_seek_frame(this.pFormatCtx, this.videoStream, (long)num2, flags);
  238. this.readCursor = 0;
  239. this.writeCursor = 0;
  240. }
  241. }
  242. public double CurrentTime
  243. {
  244. get
  245. {
  246. return this.currentDisplayTime;
  247. }
  248. }
  249. public int height
  250. {
  251. get
  252. {
  253. return this.codecCtx.height;
  254. }
  255. }
  256. public double Length
  257. {
  258. get
  259. {
  260. long duration = this.stream.duration;
  261. if (duration < 0L)
  262. {
  263. return 36000000.0;
  264. }
  265. return (duration * this.FrameDelay);
  266. }
  267. }
  268. private double startTimeMs
  269. {
  270. get
  271. {
  272. return ((0x3e8L * this.stream.start_time) * this.FrameDelay);
  273. }
  274. }
  275. public int width
  276. {
  277. get
  278. {
  279. return this.codecCtx.width;
  280. }
  281. }
  282. public delegate void EndOfFileHandler();
  283. }
  284. }