package org.jcodec.testing; import static org.jcodec.codecs.h264.H264Utils.splitMOVPacket; import static org.jcodec.common.JCodecUtil.getAsIntArray; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jcodec.codecs.h264.H264Decoder; import org.jcodec.codecs.h264.H264Utils; import org.jcodec.codecs.h264.mp4.AvcCBox; import org.jcodec.common.FileChannelWrapper; import org.jcodec.common.IOUtils; import org.jcodec.common.NIOUtils; import org.jcodec.common.SeekableByteChannel; import org.jcodec.common.model.ColorSpace; import org.jcodec.common.model.Packet; import org.jcodec.common.model.Picture; import org.jcodec.containers.mp4.boxes.Box; import org.jcodec.containers.mp4.boxes.LeafBox; import org.jcodec.containers.mp4.boxes.VideoSampleEntry; import org.jcodec.containers.mp4.demuxer.MP4Demuxer; import org.jcodec.containers.mp4.demuxer.AbstractMP4DemuxerTrack; public class TestTool { private String jm; private File coded; private File decoded; private File jmconf; private File errs; public TestTool(String jm, String errs) throws IOException { this.jm = jm; this.errs = new File(errs); coded = File.createTempFile("seq", ".264"); decoded = File.createTempFile("seq_dec", ".yuv"); jmconf = File.createTempFile("ldecod", ".conf"); prepareJMConf(); } public static void main(String[] args) throws Exception { if (args.length != 3) { System.out.println("JCodec h.264 test tool"); System.out.println("Syntax: <path to ldecod> <movie file> <foder for errors>"); return; } new TestTool(args[0], args[2]).doIt(args[1]); } private void doIt(String in) throws Exception { SeekableByteChannel raw = null; SeekableByteChannel source = null; try { source = new FileChannelWrapper(new FileInputStream(in).getChannel()); MP4Demuxer demux = new MP4Demuxer(source); H264Decoder decoder = new H264Decoder(); AbstractMP4DemuxerTrack inTrack = demux.getVideoTrack(); VideoSampleEntry ine = (VideoSampleEntry) inTrack.getSampleEntries()[0]; AvcCBox avcC = Box.as(AvcCBox.class, Box.findFirst(ine, LeafBox.class, "avcC")); ByteBuffer _rawData = ByteBuffer.allocate(1920 * 1088 * 6); decoder.addSps(avcC.getSpsList()); decoder.addPps(avcC.getPpsList()); Packet inFrame; int sf = 2600; AbstractMP4DemuxerTrack dt = (AbstractMP4DemuxerTrack) inTrack; dt.gotoFrame(sf); while ((inFrame = inTrack.nextFrame()) != null && !inFrame.isKeyFrame()) ; // System.out.println(inFrame.getFrameNo() + " - " + // inFrame.isKeyFrame()); dt.gotoFrame(inFrame.getFrameNo()); List<Picture> decodedPics = new ArrayList<Picture>(); int totalFrames = (int) inTrack.getFrameCount(), seqNo = 0; for (int i = sf; (inFrame = inTrack.nextFrame()) != null; i++) { ByteBuffer data = inFrame.getData(); List<ByteBuffer> nalUnits = splitMOVPacket(data, avcC); _rawData.clear(); H264Utils.joinNALUnits(nalUnits, _rawData); _rawData.flip(); if (H264Utils.idrSlice(_rawData)) { if (raw != null) { raw.close(); runJMCompareResults(decodedPics, seqNo); decodedPics = new ArrayList<Picture>(); seqNo = i; } raw = new FileChannelWrapper(new FileOutputStream(coded).getChannel()); H264Utils.saveStreamParams(avcC, raw); } raw.write(_rawData); decodedPics.add(decoder.decodeFrame(nalUnits, Picture.create((ine.getWidth() + 15) & ~0xf, (ine.getHeight() + 15) & ~0xf, ColorSpace.YUV420) .getData())); if (i % 500 == 0) System.out.println((i * 100 / totalFrames) + "%"); } if (decodedPics.size() > 0) runJMCompareResults(decodedPics, seqNo); } finally { if (source != null) source.close(); if (raw != null) raw.close(); } } private void runJMCompareResults(List<Picture> decodedPics, int seqNo) throws Exception { try { Process process = Runtime.getRuntime().exec(jm + " -d " + jmconf.getAbsolutePath()); process.waitFor(); ByteBuffer yuv = NIOUtils.fetchFrom(decoded); for (Picture pic : decodedPics) { pic = pic.cropped(); boolean equals = Arrays.equals(getAsIntArray(yuv, pic.getPlaneWidth(0) * pic.getPlaneHeight(0)), pic.getPlaneData(0)); equals &= Arrays.equals(getAsIntArray(yuv, pic.getPlaneWidth(1) * pic.getPlaneHeight(1)), pic.getPlaneData(1)); equals &= Arrays.equals(getAsIntArray(yuv, pic.getPlaneWidth(2) * pic.getPlaneHeight(2)), pic.getPlaneData(2)); if (!equals) diff(seqNo); } } catch (Exception e) { diff(seqNo); } } private void diff(int seqNo) { System.out.println(seqNo + ": DIFF!!!"); coded.renameTo(new File(errs, String.format("seq%08d.264", seqNo))); decoded.renameTo(new File(errs, String.format("seq%08d_dec.yuv", seqNo))); } private void prepareJMConf() throws IOException { InputStream cool = null; try { cool = getClass().getClassLoader().getResourceAsStream("org/jcodec/testing/jm.conf"); String str = IOUtils.toString(cool); str = str.replace("%input_file%", coded.getAbsolutePath()); str = str.replace("%output_file%", decoded.getAbsolutePath()); IOUtils.writeStringToFile(jmconf, str); } finally { IOUtils.closeQuietly(cool); } } }