2013-07-31 2 views
1

Я работаю над приложением Java, которое будет передавать видео с IP-камеры. Видеопотоки с 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»? Вот мой текущий (сломанный) код.

EDIT: Я получил его на работу. Я обновил код ниже. Тем не менее, это очень медленно. Я не уверен, что это имеет какое-либо отношение к ArrayList или нет, но это может быть преступником. Любые указатели на ускорение кода? В настоящее время он принимает от 500 мс до 900 мс для одного кадра.

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 [], lol. Есть ли другой способ прочитать, пока не будет найден «\ r \ n»? Я бы предпочел не использовать readLine() в DataInputStream, поскольку он устарел. Я пробовал использовать класс Scanner, но это похоже на BufferedReader и предназначено для текста. – CharDev

+0

DataInputStream.readLine() устарел, потому что он читает текст, но предполагаю, что кодировка ISO-8859-1, которую я подозреваю, в порядке.Вы можете написать свой собственный метод readLine(), но я думаю, что он в конечном итоге сделает то же самое. Иногда самым простым выбором является устаревший метод. –

+0

У меня есть работа с ArrayList с чтением заголовков, а затем чтение остального в байт [] после того, как он получит длину и находит «\ r \ n». Однако он очень медленный (500 мс-900 мс). Любые указатели? – 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

Смежные вопросы