2015-01-23 2 views
1

Я пытаюсь сделать приложение для Android, которое отправит вывод камеры на сервер как поток rtp, но он не работает должным образом. я делаю следующие шагиНевозможно отправить видео как поток rtp с android

  1. В классе активность реализуется SurfaceTextureListener интерфейс и в onCreate() создан TextureView и добавил слушателя.

  2. В public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) метод создан и инициализирован Camera и MediaCodec экземпляр для кодирования выхода камеры к H.264. Также добавлены PreviewCallback для камеры следующим образом -

    mCamera.setPreviewCallback(new Camera.PreviewCallback() { 
    
        @Override 
        public void onPreviewFrame(byte[] data, Camera camera) { 
         // Here encode method will encode frame using Mediacodec and send it to LocalSocket. 
         encode(data); 
        } 
    }); 
    
  3. Теперь другой AsyncTask прочтет эту LocalSocket и отправить его в DatagramSocket путем добавления RTP заголовка в каждом пакете.

  4. Я тестирую этот код на VLC, передавая файл sdp, но VLC не воспроизводит видео. Если я открою udp-сокет в VLC udp://@:5001 , тогда в Media Information VLC отображаются некоторые данные в «Read At Media» и «Input Bitrate», это означает, что мое приложение отправляет некоторые данные в этот udp-порт. Также я попытался сохранить видео в Android-устройстве, и мое приложение сохраняет правильное видео с того же кода MediaCoded и Camera.

RTP заголовка и пакетов кода формирования

int Version; // 2 bits 
int Padding; // 1 bit 
int Extension; // 1 bit 
int CC; // 4 bits 
int Marker; // 1 bit 
int PayloadType=96; // 7 bits 
int Ssrc; // 32 bits 
Version = 2; 
Padding = 0; 
Extension = 0; 
CC = 0; 
Marker = 0; 
Ssrc = 0; 
byte[] header = new byte[ 12 ]; 
long timeStamp = System.currentTimeMillis(); 
mSeq = ++mSeq + 1; 
header[0] = (byte)(Version << 6); 
header[0] = (byte)(header[0] | Padding << 5); 
header[0] = (byte)(header[0] | Extension << 4); 
header[0] = (byte)(header[0] | CC); 
header[1] = (byte)(header[1] | Marker << 7); 
header[1] = (byte)(header[1] | PayloadType); 
header[2] = (byte)(mSeq >> 8); 
header[3] = (byte)(mSeq & 0xFF); 
header[4] = (byte)(timeStamp >> 24); 
header[5] = (byte)(timeStamp >> 16); 
header[6] = (byte)(timeStamp >> 8); 
header[7] = (byte)(timeStamp & 0xFF); 
header[8] = (byte)(Ssrc >> 24); 
header[9] = (byte)(Ssrc >> 16); 
header[10] = (byte)(Ssrc >> 8); 
header[11] = (byte)(Ssrc & 0xFF); 
mBuffers = new byte[1400]; 
System.arraycopy(header, 0, mBuffers, 0, header.length); 
System.arraycopy(buf, 0, mBuffers, 12, buf.length); 
DatagramPacket out = new DatagramPacket(mBuffers, mBuffers.length, hostAddress, 5001); 
socket.send(out); 

Я пытался исправить мой код, удаляя первые 4 байта пакета, как кто-то из StackOverflow говорит, что в АВК нам нужно удалить 1-го по 4 байта. Также дважды проверял мой заголовок RTP, но не повезло ...

Любая идея, почему мой код не отправляет видео как rtp?

+0

https://code.google.com/p/spydroid-ipcamera/ искать, где он устанавливает пакеты в rtp –

ответ

1

Вы не можете просто добавить заголовок RTP, вам также необходимо переформатировать закодированные буферы, чтобы они вписывались в один или несколько RTP-пакетов фиксированной длины (ака «пакетирование») для формата полезной нагрузки H264 RTP, см. RFC 6184 для полная спецификация.

Если пакеты H.264 достаточно коротки, чтобы соответствовать размеру пакета 1400, то да, достаточно просто удалить первые 4 байта (при условии, что первые 4 байта равны 0, 0, 0, 1). Если выходной буфер из кодера содержит более одного блока NAL (если в буфере имеется больше записей последовательности [0, 0, 0, 1]), вам нужно либо отправить каждый блок NAL в отдельный пакет , или использовать одну из более сложных схем пакетирования, см. RFC для получения более подробной информации об этом.

Во-вторых, в настоящее время вы отправляете полный пакет 1400 байт, даже если фактическая кодированная полезная нагрузка была короче. Я не уверен, сколько проблем это может вызвать, или если это может пройти незамеченным, но вы действительно должны отправлять только столько байтов, сколько вы на самом деле заполняли. (То есть вместо mBuffers.length, используйте 12 + buf.length.)

1

может быть другой подход, помимо взятия пакетирования и сетевого протокола RFP, охватывающего протокол RTP.

Найдите lib, который сделает это за вас.

Этот проект построен на netty (должен быть ОК на Android).

Я упоминаю об этом, потому что некоторое время назад я смотрел на него, чтобы сделать SDP/RTP на андроиде в контексте SIP/VOIP и нашел его доступным/работоспособным.

Если вы выгораете на уровне сырой пакетизации (я бы не хотел тестировать, что w/wireShark et al over adb), вы можете просмотреть его папку ./src/test/**/session, которую я думаю получить идея о том, как работает его тестовая вещь. Вы должны легко найти материал уровня RTP, и AFAIK - материал пакетирования и материал RFP - это хорошо.

В общем, я считаю, что вы расширили бы «сеанс», который просто обертывает/захватывает ваши видеоканалы/потоки, где его примеры могут выполнять пакетирование голоса/RTP.