2013-07-31 56 views
1

我正在處理將從IP攝像頭流式傳輸視頻的Java應用程序。來自IP攝像機的視頻以MJPEG格式傳輸。該協議是以下...讀取二進制流,直到遇到「 r n」

--ipcamera (\r\n) 
Content-Type: image/jpeg (\r\n) 
Content-Length: {length of frame} (\r\n) 
(\r\n) 
{frame} 
(\r\n) 
--ipcamera (\r\n) 
etc. 

我已經使用了類如的BufferedReader和掃描器讀取,直到「\ r \ n」個嘗試,但那些都是爲了文本而不是二進制數據,所以它變得腐敗。有沒有什麼方法可以讀取二進制流,直到遇到「\ r \ n」?這是我目前的(破碎的)代碼。

編輯:我已經得到它的工作。我更新了下面的代碼。但是,這樣做確實很慢。我不確定它是否與ArrayList有關,但它可能是罪魁禍首。任何指針來加速代碼?目前對於單幀來說,它需要500ms到900ms。

public void run() { 
    long startTime = System.currentTimeMillis(); 
    try { 
     URLConnection urlConn = url.openConnection(); 
     urlConn.setReadTimeout(15000); 
     urlConn.connect(); 
     urlStream = urlConn.getInputStream(); 
     DataInputStream dis = new DataInputStream(urlStream); 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     ArrayList<Byte> bytes = new ArrayList<Byte>(); 
     byte cur; 
     int curi; 
     byte[] curBytes; 
     int length = 0; 
     while ((curi = dis.read()) != -1) { 
      cur = (byte) curi; 
      bytes.add(cur); 
      curBytes = getPrimativeArray(bytes); 
      String curBytesString = new String(curBytes, "UTF-8"); 
      if (curBytesString.equals("--ipcamera\r\n")) { 
       bytes.clear(); 
       continue; 
      } else if (curBytesString.equals("Content-Type: image/jpeg\r\n")) { 
       bytes.clear(); 
       continue; 
      } else if (curBytesString.matches("^Content-Length: ([0-9]+)\r\n$")) { 
       length = Integer.parseInt(curBytesString.replace("Content-Length: ", "").trim()); 
       bytes.clear(); 
       continue; 
      } else if (curBytesString.equals("\r\n")) { 
       if (length == 0) { 
        continue; 
       } 
       byte[] frame = new byte[length]; 
       dis.readFully(frame, 0, length); 
       writeFrame(frame); 
       bytes.clear(); 
       break; 
      } 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    long curTime = System.currentTimeMillis() - startTime; 
    System.out.println(curTime); 
} 

private byte[] getPrimativeArray(ArrayList<Byte> array) { 
    byte[] bytes = new byte[array.size()]; 
    for (int i = 0; i < array.size(); i++) { 
     bytes[i] = array.get(i).byteValue(); 
    } 
    return bytes; 
} 

private void writeFrame(byte[] bytes) throws IOException { 
    File file = new File("C:\\test.jpg"); 
    FileOutputStream fos = new FileOutputStream(file); 
    fos.write(bytes); 
    fos.close(); 
    System.out.println("done"); 
} 

回答

3

目前,您不應對在幀部分讀取數據的情況。

粗略的假設是:

當前版本:

else if (line.equals("") && length != 0) 

可能更正確的版本:

else if (!line.equals("") && length != 0) 
2

不能使用的BufferedReader讀取二進制,它會破壞它。我想保持簡單,使用DataInputStream.readLine()。雖然不理想,但可能對您而言最簡單。

+0

是的,我沒有意識到BufferedReader只用於文本。這是有道理的考慮它返回一個char [],哈哈。除非找到「\ r \ n」,否則還有其他閱讀方式嗎?我不希望在DataInputStream中使用readLine(),因爲它已被棄用。我嘗試過使用Scanner類,但它就像BufferedReader並且用於文本。 – CharDev

+0

DataInputStream.readLine()已被棄用,因爲它讀取文本,但假定ISO-8859-1編碼,我懷疑你的情況是好的。你可以寫你自己的readLine()方法,但我認爲它最終會做同樣的事情。有時候最簡單的選擇是不推薦使用的方法。 –

+0

我使用ArrayList 讀取頭文件,然後在得到長度並找到「\ r \ n」後將其餘的文件讀入到byte []中。但是,它非常慢(500ms-900ms)。任何指針? – CharDev

2

除了使用一些不好的做法並假設您的URLConnection正確傳遞數據之外,如果您在讀取幀數據後將長度重置爲零,則似乎可以使用您發佈的示例。

} else if (line.equals("") && length != 0) { 
    char[] buf = new char[length]; 
    reader.read(buf, 0, length); 
    baos.write(new String(buf).getBytes()); 
    //break; 
    length = 0; // <-- reset length 
} 

請注意這樣所有的幀數據連續寫入相同的ByteArrayOutputStream。如果你不想要,你應該爲你遇到的每一個新幀創建一個新的ByteArrayOutputStream

1
  1. 您不能使用BufferedReader爲傳輸的一部分,然後一些其他流爲它的其餘部分。 BufferedReader將填充其緩衝區並竊取您想要與其他流一起讀取的一些數據。使用DataInputStream.readLine(),注意它已被棄用,否則使用由URLConnection.

  2. 提供的輸入流滾動您自己的行讀取代碼當然,您不必? URLConnection爲您讀取標題。如果你想要的內容長度,使用API​​來獲取它。你閱讀的東西從傳輸的主體開始。

+0

標題不是HTTP標題,它們是MJPEG分隔符,用於分隔每個單獨的JPEG圖像。 – CharDev