package org.jcodec.testing; import static org.jcodec.common.ArrayUtil.toByteArrayShifted; 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.List; import org.jcodec.codecs.h264.H264Decoder; import org.jcodec.codecs.h264.H264Utils; import org.jcodec.common.JCodecUtil2; import org.jcodec.common.SeekableDemuxerTrack; import org.jcodec.common.io.FileChannelWrapper; import org.jcodec.common.io.IOUtils; import org.jcodec.common.io.NIOUtils; import org.jcodec.common.io.SeekableByteChannel; import org.jcodec.common.model.ColorSpace; import org.jcodec.common.model.Packet; import org.jcodec.common.model.Picture8Bit; import org.jcodec.common.model.Size; import org.jcodec.containers.mp4.demuxer.MP4Demuxer; import org.jcodec.platform.Platform; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * @author The JCodec project * */ 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 main1(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); SeekableDemuxerTrack inTrack = (SeekableDemuxerTrack) demux.getVideoTrack(); ByteBuffer _rawData = ByteBuffer.allocate(1920 * 1088 * 6); ByteBuffer codecPrivate = inTrack.getMeta().getCodecPrivate(); H264Decoder decoder = H264Decoder.createH264DecoderFromCodecPrivate(codecPrivate); Packet inFrame; int sf = 2600; inTrack.gotoFrame(sf); while ((inFrame = inTrack.nextFrame()) != null && !inFrame.isKeyFrame()) ; inTrack.gotoFrame(inFrame.getFrameNo()); List<Picture8Bit> decodedPics = new ArrayList<Picture8Bit>(); int totalFrames = (int) inTrack.getMeta().getTotalFrames(), seqNo = 0; for (int i = sf; (inFrame = inTrack.nextFrame()) != null; i++) { ByteBuffer data = inFrame.getData(); List<ByteBuffer> nalUnits = H264Utils.splitFrame(data); _rawData.clear(); H264Utils.joinNALUnitsToBuffer(nalUnits, _rawData); _rawData.flip(); if (H264Utils.isByteBufferIDRSlice(_rawData)) { if (raw != null) { raw.close(); runJMCompareResults(decodedPics, seqNo); decodedPics = new ArrayList<Picture8Bit>(); seqNo = i; } raw = new FileChannelWrapper(new FileOutputStream(coded).getChannel()); raw.write(codecPrivate); } raw.write(_rawData); Size size = inTrack.getMeta().getVideoCodecMeta().getSize(); decodedPics.add(decoder.decodeFrame8BitFromNals(nalUnits, Picture8Bit .create((size.getWidth() + 15) & ~0xf, (size.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<Picture8Bit> decodedPics, int seqNo) throws Exception { try { Process process = Runtime.getRuntime().exec(jm + " -d " + jmconf.getAbsolutePath()); process.waitFor(); ByteBuffer yuv = NIOUtils.fetchFromFile(decoded); for (Picture8Bit pic : decodedPics) { pic = pic.cropped(); boolean equals = Platform.arrayEqualsByte( toByteArrayShifted(JCodecUtil2.getAsIntArray(yuv, pic.getPlaneWidth(0) * pic.getPlaneHeight(0))), pic.getPlaneData(0)); equals &= Platform.arrayEqualsByte( toByteArrayShifted(JCodecUtil2.getAsIntArray(yuv, pic.getPlaneWidth(1) * pic.getPlaneHeight(1))), pic.getPlaneData(1)); equals &= Platform.arrayEqualsByte( toByteArrayShifted(JCodecUtil2.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 = Platform.getResourceAsStream(getClass(), "org/jcodec/testing/jm.conf"); String str = IOUtils.readToString(cool); str = str.replace("%input_file%", coded.getAbsolutePath()); str = str.replace("%output_file%", decoded.getAbsolutePath()); IOUtils.writeStringToFile(jmconf, str); } finally { IOUtils.closeQuietly(cool); } } }