无法从Android发送视频作为rtp流

时间:2015-01-23 06:26:14

标签: android android-camera rtp mediacodec

我正在尝试制作Android应用程序,它会将摄像头输出作为rtp流发送到服务器,但它没有按预期工作。 我正在做以下步骤

  1. 在Activity类中实现了SurfaceTextureListener接口,并在onCreate()创建了TextureView并添加了监听器。

  2. public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)方法中创建并初始化CameraMediaCodec实例,将摄像头输出编码为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并通过在每个数据包中添加RTP标头将其发送到DatagramSocket

  4. 我通过提供sdp文件在VLC上测试此代码,但VLC没有播放任何视频。如果我在VLC中打开udp套接字udp://@:5001 然后在媒体信息中,VLC在“Read At Media”和“Input Bitrate”中显示一些数据,这意味着我的应用程序正在向该udp端口发送一些数据。 此外,我尝试将视频保存到Android设备,我的应用正在保存来自相同MediaCodedCamera代码的正确视频。
  5. 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说在AVC中我们需要删除前4个字节。还检查了我的RTP标题两次,但没有运气......

    知道为什么我的代码无法以rtp发送视频?

2 个答案:

答案 0 :(得分:1)

您不能只添加RTP标头,还需要重新格式化编码缓冲区以适合H264 RTP有效载荷格式的一个或多个固定长度RTP数据包(又名“打包”),请参阅{{3}完整的规范。

如果H.264数据包足够短以适应1400数据包大小,那么是的,只需删除前4个字节(假设前4个字节为0,0,0,1)。如果来自编码器的输出缓冲区包含多个NAL单元(如果缓冲区中序列[0,] 0,0,1更多出现),那么您需要在单独的数据包中发送每个NAL单元或者使用一种更复杂的分组方案,请参阅RFC以获取更多详细信息。

其次,即使实际编码的有效负载较短,目前您正在发送完整的1400字节数据包。我不确定它会导致多少问题,或者它是否可以被忽视,但你真的应该只发送实际填充的字节数。 (即代替mBuffers.length,使用12 + buf.length。)

答案 1 :(得分:1)

除了自己承担RTP协议的分组化和网络RFP之外,可能还有另一种方法。

找一个为你做的lib

这个项目建立在netty上(在Android上应该没问题。)

我提到它是因为我前段时间看过它在SIP / VOIP上下文中对android进行SDP / RTP并发现它是可维修/可行的。

如果你在原始打包级别烧坏(我不想测试w / wireShark et al over adb)你可以查看他的./src/test/**/session文件夹我认为得到一个他的测试内容如何运行的想法。你应该能够很容易地找到RTP级别的东西,AFAIK的包装化和RFP的东西是好的。

总的来说,我相信你会扩展某种类型的会话&#34;只是包装/挂钩您的视频通道/流,他的示例可能正在进行语音/ RTP打包。