字节数组未完全消耗

时间:2014-03-05 17:40:25

标签: java arrays file-io bytearray fileinputstream

我正在用Java编写FLV解析器并遇到了一个问题。该程序成功地将标记分析并分组到数据包中,并根据标题中的BodyLength标记为每个标记的主体正确识别和分配字节数组。但是在我的测试文件中,它成功完成了此操作,但在最后4个字节之前停止。

第一个文件中遗漏的字节序列是:

00 00 14 C3

在第二个:

00 00 01 46

显然这是两个文件的最后4个字节的问题但是我无法在逻辑中发现错误。我怀疑它可能是:

while (in.available() != 0)

但是我也怀疑是这种情况,因为程序成功进入最终标签的循环,但它只是停止4个字节短。任何帮助是极大的赞赏。 (我知道正确的异常处理尚未发生)

Parser.java

    import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Array;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;

/**
 * 
 * @author A
 * 
 *      Parser class for FLV files
 */

public class Parser {

    private static final int HEAD_SIZE = 9;
    private static final int TAG_HEAD_SIZE = 15;
    private static final byte[] FLVHEAD = { 0x46, 0x4C, 0x56 };
    private static final byte AUDIO = 0x08;
    private static final byte VIDEO = 0x09;
    private static final byte DATA = 0x12;
    private static final int TYPE_INDEX = 4;

    private File file;
    private FileInputStream in;
    private ArrayList<Packet> packets;
    private byte[] header = new byte[HEAD_SIZE];

    Parser() throws FileNotFoundException {
        throw new FileNotFoundException();
    }

    Parser(URI uri) {
        file = new File(uri);
        init();
    }

    Parser(File file) {
        this.file = file;
        init();
    }

    private void init() {
        packets = new ArrayList<Packet>();
    }

    public void parse() {
        boolean test = false;
        try {
            test = parseHeader();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (test) {
            System.out.println("Header Verified");
            // Add header packet to beginning of list & then null packet
            Packet p = new Packet(PTYPE.P_HEAD);
            p.setSize(header.length);
            p.setByteArr(header);
            packets.add(p);
            p = null;

            try {
                parseTags();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            try {
                in.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // throw FileNotFoundException because incorrect file
        }
    }

    private boolean parseHeader() throws FileNotFoundException, IOException {
        if (file == null)
            throw new FileNotFoundException();

        in = new FileInputStream(file);
        in.read(header, 0, 9);

        return Arrays.equals(FLVHEAD, Arrays.copyOf(header, FLVHEAD.length));
    }

    private void parseTags() throws IOException {
        if (file == null)
            throw new FileNotFoundException();
        byte[] tagHeader = new byte[TAG_HEAD_SIZE];
        Arrays.fill(tagHeader, (byte) 0x00);
        byte[] body;
        byte[] buf;
        PTYPE pt;

        int OFFSET = 0;
        while (in.available() != 0) {
            // Read first 5 - bytes, previous tag size + tag type
            in.read(tagHeader, 0, 5);

            if (tagHeader[TYPE_INDEX] == AUDIO) {
                pt = PTYPE.P_AUD;
            } else if (tagHeader[TYPE_INDEX] == VIDEO) {
                pt = PTYPE.P_VID;
            } else if (tagHeader[TYPE_INDEX] == DATA) {
                pt = PTYPE.P_DAT;
            } else {
                // Header should've been dealt with - if previous data types not
                // found then throw exception
                System.out.println("Unexpected header format: ");
                System.out.print(String.format("%02x\n", tagHeader[TYPE_INDEX]));
                System.out.println("Last Tag");
                packets.get(packets.size()-1).diag();
                System.out.println("Number of tags found: " + packets.size());
                throw new InputMismatchException();
            }

            OFFSET = TYPE_INDEX;

            // Read body size - 3 bytes
            in.read(tagHeader, OFFSET + 1, 3);
            // Body size buffer array - padding for 1 0x00 bytes
            buf = new byte[4];
            Arrays.fill(buf, (byte) 0x00);
            // Fill size bytes
            buf[1] = tagHeader[++OFFSET];
            buf[2] = tagHeader[++OFFSET];
            buf[3] = tagHeader[++OFFSET];
            // Calculate body size
            int bSize = ByteBuffer.wrap(buf).order(ByteOrder.BIG_ENDIAN)
                    .getInt();

            // Initialise Array
            body = new byte[bSize];

            // Timestamp
            in.read(tagHeader, ++OFFSET, 3);
            Arrays.fill(buf, (byte) 0x00);
            // Fill size bytes
            buf[1] = tagHeader[OFFSET++];
            buf[2] = tagHeader[OFFSET++];
            buf[3] = tagHeader[OFFSET++];
            int milliseconds = ByteBuffer.wrap(buf).order(ByteOrder.BIG_ENDIAN)
                    .getInt();
            // Read padding
            in.read(tagHeader, OFFSET, 4);
            // Read body
            in.read(body, 0, bSize);

            // Diagnostics
            //printBytes(body);

            Packet p = new Packet(pt);
            p.setSize(tagHeader.length + body.length);
            p.setByteArr(concat(tagHeader, body));
            p.setMilli(milliseconds);
            packets.add(p);
            p = null;

            // Zero out for next iteration
            body = null;
            Arrays.fill(buf, (byte)0x00);
            Arrays.fill(tagHeader, (byte)0x00);
            milliseconds = 0;
            bSize = 0;
            OFFSET = 0;
        }

        in.close();
    }

    private byte[] concat(byte[] tagHeader, byte[] body) {
        int aLen = tagHeader.length;
        int bLen = body.length;

        byte[] C = (byte[]) Array.newInstance(tagHeader.getClass()
                .getComponentType(), aLen + bLen);
        System.arraycopy(tagHeader, 0, C, 0, aLen);
        System.arraycopy(body, 0, C, aLen, bLen);
        return C;
    }

    private void printBytes(byte[] b) {
        System.out.println("\n--------------------");
        for (int i = 0; i < b.length; i++) {
            System.out.print(String.format("%02x ", b[i]));
            if (((i % 8) == 0 ) && i != 0)
                System.out.println();
        }
    }

}

Packet.java

public class Packet {

    private PTYPE type = null;
    byte[] buf;
    int milliseconds;

    Packet(PTYPE t) {
        this.setType(t);
    }

    public void setSize(int s) {
        buf = new byte[s];
    }

    public PTYPE getType() {
        return type;
    }

    public void setType(PTYPE type) {
        if (this.type == null)
            this.type = type;
    }

    public void setByteArr(byte[] b) {
        this.buf = b;
    }

    public void setMilli(int milliseconds) {
        this.milliseconds = milliseconds;
    }

    public void diag(){
        System.out.println("|-- Tag Type: " + type);
        System.out.println("|-- Milliseconds: " + milliseconds);
        System.out.println("|-- Size: " + buf.length);
        System.out.println("|-- Bytes: ");
        for(int i = 0; i < buf.length; i++){
            System.out.print(String.format("%02x ", buf[i]));
            if (((i % 8) == 0 ) && i != 0)
                System.out.println();
        }
        System.out.println();
    }
}

jFLV.java

import java.net.URISyntaxException;

public class jFLV {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Parser p = null;
        try {
            p = new Parser(jFLV.class.getResource("sample.flv").toURI());
        } catch (URISyntaxException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        p.parse();

    }

}

PTYPE.java

public enum PTYPE {
    P_HEAD,P_VID,P_AUD,P_DAT
};

2 个答案:

答案 0 :(得分:2)

您对available()的使用和对read的通话都被破坏了。不可否认,我会有点期望这对FileInputStream没问题(直到你到达流的末尾,此时忽略read的返回值仍然可能是灾难性的)但我个人认为流总是返回部分数据。

available()只会告诉您现在是否有可用的数据。它非常很少有用 - 只需忽略它。如果要读取直到流的末尾,通常应该继续调用read,直到它返回-1。诚然,把它与“我正在尝试阅读下一个街区”结合起来有点棘手。 (如果InputStreampeek()方法,那就太好了,但事实并非如此。您可以将其包装在BufferedInputStream中并使用mark / reset在每个循环的开始测试...丑陋,但它应该工作。)

接下来,你忽略了InputStream.read的结果(在多个地方)。您总是使用此结果,而不是假设它已经读取了您要求的数据量。您可能需要一些辅助方法,例如

static byte[] readExactly(InputStream input, int size) throws IOException {
    byte[] data = new byte[size];
    readExactly(input, data);
    return data;
}

static void readExactly(InputStream input, byte[] data) throws IOException {
    int index = 0;
    while (index < data.length) {
        int bytesRead = input.read(data, index, data.length - index);
        if (bytesRead < 0) {
            throw new EOFException("Expected more data");
        }
    }
}

答案 1 :(得分:0)

您应该使用其中一种read方法而不是available,因为available()“返回可以从此输入流中读取(或跳过)的字节数的估计值,而不会在下次调用方法时阻塞对于此输入流。“

它不是为了检查您可以阅读的时间。