ThatGamerBlue2

Untitled

Jun 25th, 2020
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.55 KB | None | 0 0
  1. package com.thatgamerblue.jcodectest;
  2.  
  3. import java.awt.image.BufferedImage;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.nio.ByteBuffer;
  7. import java.util.Map;
  8. import java.util.TreeMap;
  9. import java.util.concurrent.Executors;
  10. import java.util.concurrent.ScheduledExecutorService;
  11. import org.jcodec.codecs.h264.H264Decoder;
  12. import org.jcodec.codecs.h264.H264Utils;
  13. import org.jcodec.codecs.h264.io.model.NALUnit;
  14. import org.jcodec.codecs.h264.io.model.NALUnitType;
  15. import org.jcodec.codecs.h264.io.model.SeqParameterSet;
  16. import org.jcodec.common.AutoFileChannelWrapper;
  17. import org.jcodec.common.Codec;
  18. import org.jcodec.common.DemuxerTrackMeta;
  19. import org.jcodec.common.SeekableDemuxerTrack;
  20. import org.jcodec.common.model.ColorSpace;
  21. import org.jcodec.common.model.Packet;
  22. import org.jcodec.common.model.Picture;
  23. import org.jcodec.common.model.Size;
  24. import org.jcodec.containers.mkv.demuxer.MKVDemuxer;
  25. import org.jcodec.javase.scale.AWTUtil;
  26.  
  27. /**
  28. * @author ThatGamerBlue
  29. * Licensed under WTFPL, see: http://www.wtfpl.net/
  30. */
  31. public class MKVDecoder
  32. {
  33. private JCodecMKVFrameGrab frameGrab;
  34.  
  35. public MKVDecoder(final File file, final boolean repeating)
  36. {
  37. try
  38. {
  39. frameGrab = new JCodecMKVFrameGrab(new AutoFileChannelWrapper(file), repeating);
  40. }
  41. catch (IOException | IllegalArgumentException e)
  42. {
  43. System.err.printf("Failed to open file %s for reading MKV video%n", file.toString());
  44. e.printStackTrace();
  45. }
  46. }
  47.  
  48. public BufferedImage getNextFrame()
  49. {
  50. try
  51. {
  52. return frameGrab.getFrame();
  53. }
  54. catch (IOException e)
  55. {
  56. System.err.printf("Failed to read frame from MKV video using JCodec%n");
  57. e.printStackTrace();
  58. }
  59. return null;
  60. }
  61.  
  62. static class JCodecMKVFrameGrab
  63. {
  64. private static final int PRELOAD_FRAMES = 9;
  65. // covers the most common patterns of bframes and iframes interleaved
  66.  
  67. private final SeekableDemuxerTrack videoTrack;
  68. private final H264Decoder decoder;
  69. private final TreeMap<Double, BufferedImage> preloadedFrames;
  70. private final TreeMap<Double, BufferedImage> initialFrames;
  71. private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
  72. private final boolean repeating;
  73.  
  74. private byte[][] buffers;
  75.  
  76. public JCodecMKVFrameGrab(AutoFileChannelWrapper afcw, boolean repeating) throws IOException
  77. {
  78. this.repeating = repeating;
  79.  
  80. MKVDemuxer demuxer = new MKVDemuxer(afcw);
  81.  
  82. if (demuxer.getVideoTracks().isEmpty())
  83. {
  84. throw new IllegalArgumentException("MKV file has no video track.");
  85. }
  86.  
  87. videoTrack = (SeekableDemuxerTrack) demuxer.getVideoTracks().get(0);
  88.  
  89. if (videoTrack.getMeta().getCodec() != Codec.H264)
  90. {
  91. throw new IllegalArgumentException("MKV file must be in H264 format.");
  92. }
  93.  
  94. decoder = H264Decoder.createH264DecoderFromCodecPrivate(videoTrack.getMeta().getCodecPrivate());
  95. preloadedFrames = new TreeMap<>(Double::compare);
  96. initialFrames = new TreeMap<>(Double::compare);
  97.  
  98. bufferFrames();
  99. }
  100.  
  101. private void bufferFrames() throws IOException
  102. {
  103. boolean loadingFirst = false;
  104. if (initialFrames.isEmpty())
  105. {
  106. loadingFirst = true;
  107. }
  108.  
  109. while (preloadedFrames.size() < PRELOAD_FRAMES)
  110. {
  111. Packet frame = videoTrack.nextFrame();
  112. if (frame == null)
  113. {
  114. break;
  115. }
  116.  
  117. BufferedImage image = AWTUtil.toBufferedImage(decoder.decodeFrame(frame.getData(), getBuffer()));
  118. preloadedFrames.put(frame.getPtsD(), image);
  119. if (loadingFirst)
  120. {
  121. initialFrames.put(frame.getPtsD(), image);
  122. }
  123. }
  124. }
  125.  
  126. public BufferedImage getFrame() throws IOException
  127. {
  128. // deals with interleaved frames, preventing jittery playback
  129. bufferFrames();
  130.  
  131. Map.Entry<Double, BufferedImage> retn = preloadedFrames.firstEntry();
  132. if (retn == null)
  133. {
  134. // we're at the end of the buffer
  135. if (!repeating)
  136. {
  137. return null;
  138. }
  139.  
  140. preloadedFrames.putAll(initialFrames);
  141. // skip initial frames when next buffering
  142. videoTrack.gotoFrame(initialFrames.size());
  143. // refresh loaded frame
  144. retn = preloadedFrames.firstEntry();
  145. }
  146. preloadedFrames.remove(retn.getKey());
  147. return retn.getValue();
  148. }
  149.  
  150. private byte[][] getBuffer()
  151. {
  152. if (buffers == null)
  153. {
  154. Size size = calcBufferSize();
  155. buffers = Picture.create(size.getWidth(), size.getHeight(), ColorSpace.YUV444).getData();
  156. }
  157. return buffers;
  158. }
  159.  
  160. private Size calcBufferSize()
  161. {
  162. // declare output variables
  163. int w = Integer.MIN_VALUE;
  164. int h = Integer.MIN_VALUE;
  165.  
  166. // get track metadata
  167. DemuxerTrackMeta meta = videoTrack.getMeta();
  168.  
  169. // allocate buffers to hold metadata
  170. ByteBuffer codecBuffer = meta.getCodecPrivate().duplicate();
  171. ByteBuffer nalBuffer;
  172.  
  173. // search the metadata for the size units
  174. while ((nalBuffer = H264Utils.nextNALUnit(codecBuffer)) != null)
  175. {
  176. // read unit from the nalBuffer
  177. NALUnit unit = NALUnit.read(nalBuffer);
  178.  
  179. // only interested in Sequence Parameter Set units
  180. if (unit.type != NALUnitType.SPS)
  181. {
  182. continue;
  183. }
  184.  
  185. // read the sps from the buffer
  186. // luckily jcodec does 99% of the work for us
  187. SeqParameterSet sps = H264Utils.readSPS(nalBuffer);
  188.  
  189. // read the width from the parameters
  190. int tempW = sps.picWidthInMbsMinus1 + 1;
  191. if (tempW > w)
  192. {
  193. w = tempW;
  194. }
  195.  
  196. // read the height from the parameters
  197. int tempH = SeqParameterSet.getPicHeightInMbs(sps);
  198. if (tempH > h)
  199. {
  200. h = tempH;
  201. }
  202. }
  203.  
  204. return new Size(w << 4, h << 4);
  205. }
  206. }
  207. }
Advertisement
Add Comment
Please, Sign In to add comment