2014-03-05 55 views
0

我正在用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

您使用available()和致電read都被打破。無可否認我會有有點預計這是可以的FileInputStream(直到你到達流的末尾,在這一點忽略read的返回值仍然可能是災難性的),但我個人認爲,流可以總是返回部分數據。

available()只告訴你是否有任何可用的數據現在。這是非常有用 - 只是忽略它。如果你想閱讀直到流結束,你通常應該繼續調用read,直到它返回-1。無可否認,將它與「我試圖讀下一個街區」結合起來有點棘手。 (如果InputStream有一個peek()方法,它會很好,但它不會,你可以將它包裝在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"); 
     } 
    } 
} 
0

您應該使用其中一種讀取方法,而不是available,因爲available()「返回可以從此輸入流讀取(或跳過)的字節數的估計值,而不會因下一次調用該輸入流的方法「。

它不是用來檢查您可以閱讀的時間。

+0

排序它謝謝你。我會在這個問題上添加更改。 – alexenk