package org.jcodec.codecs.y4m; import static org.jcodec.common.StringUtils.split; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import org.jcodec.common.NIOUtils; import org.jcodec.common.SeekableByteChannel; import org.jcodec.common.model.ColorSpace; import org.jcodec.common.model.Picture; import org.jcodec.common.model.Rational; import org.jcodec.common.model.Size; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * @author The JCodec project * */ public class Y4MDecoder { private FileChannel is; private int width; private int height; private String invalidFormat; private Rational fps; private int bufSize; public Y4MDecoder(SeekableByteChannel is) throws IOException { ByteBuffer buf = NIOUtils.fetchFrom(is, 2048); String[] header = split(readLine(buf), ' '); if (!"YUV4MPEG2".equals(header[0])) { invalidFormat = "Not yuv4mpeg stream"; return; } String chroma = find(header, 'C'); if (chroma != null && !chroma.startsWith("420")) { invalidFormat = "Only yuv420p is supported"; return; } width = Integer.parseInt(find(header, 'W')); height = Integer.parseInt(find(header, 'H')); String fpsStr = find(header, 'F'); if (fpsStr != null) { String[] numden = split(fpsStr, ':'); fps = new Rational(Integer.parseInt(numden[0]), Integer.parseInt(numden[1])); } is.position(buf.position()); bufSize = width * height * 2; } public Picture nextFrame(int[][] buffer) throws IOException { if (invalidFormat != null) throw new RuntimeException("Invalid input: " + invalidFormat); long pos = is.position(); ByteBuffer buf = NIOUtils.fetchFrom(is, 2048); String frame = readLine(buf); if (frame == null || !frame.startsWith("FRAME")) return null; MappedByteBuffer pix = is.map(MapMode.READ_ONLY, pos + buf.position(), bufSize); is.position(pos + buf.position() + bufSize); Picture create = Picture.create(width, height, ColorSpace.YUV420); copy(pix, create.getPlaneData(0)); copy(pix, create.getPlaneData(1)); copy(pix, create.getPlaneData(2)); return create; } void copy(ByteBuffer b, int[] ii) { for (int i = 0; b.hasRemaining(); i++) { ii[i] = b.get() & 0xff; } } private static String find(String[] header, char c) { for (String string : header) { if (string.charAt(0) == c) return string.substring(1); } return null; } private static String readLine(ByteBuffer y4m) { ByteBuffer duplicate = y4m.duplicate(); while (y4m.hasRemaining() && y4m.get() != '\n') ; if (y4m.hasRemaining()) duplicate.limit(y4m.position() - 1); return new String(NIOUtils.toArray(duplicate)); } public int getWidth() { return width; } public int getHeight() { return height; } public Rational getFps() { return fps; } public Size getSize() { return new Size(width, height); } }