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

Unable to send video as rtp stream from android

我正在尝试制作 android 应用程序,它将相机输出作为 rtp 流发送到服务器,但它没有按预期工作。 我正在执行以下步骤

  1. 在 Activity class 中实现了 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 Header 将其发送到 DatagramSocket

  4. 我正在通过提供 sdp 文件在 VLC 上测试此代码,但 VLC 没有播放任何视频。如果我在 VLC udp://@:5001 中打开 udp 套接字 然后在媒体信息中,VLC 在 "Read At Media" 和 "Input Bitrate" 中显示一些数据,这意味着我的应用正在向该 udp 端口​​发送一些数据。 我还尝试将视频保存到 android 设备中,我的应用正在保存来自相同 MediaCodedCamera 代码的正确视频。

RTP Header 和数据包形成代码

    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 个字节来修复我的代码,因为来自 Whosebug 的人说在 AVC 中我们需要删除第一个 4 个字节。还检查了我的 RTP header 两次,但没有运气......

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

您不能只添加 RTP header,您还需要重新格式化编码缓冲区以适应 H264 RTP 的一个或多个 fixed-length RTP 数据包(也称为“打包”)有效负载格式,请参阅 RFC 6184 了解完整规范。

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

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

除了自己承担打包和网络 RFP 的覆盖 RTP 协议之外,可能还有另一种方法。

找一个 lib 为您做这件事。

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

我提到它是因为我前段时间查看它在 SIP/VOIP 上下文中对 android 执行 SDP/RTP 并发现它是 servicable/workable。

如果您在原始打包级别上精疲力竭(我不想通过 adb 测试 wireShark 等),您可以查看他的 ./src/test/**/session 文件夹,我认为了解他的测试内容 运行。您应该能够很容易地找到 RTP 级别的东西,而且 AFAIK 打包的东西和 RFP 的东西很好。

总的来说,我相信您会扩展某种 "Session",只是 wraps/hooks 您的视频 channels/streams 他的示例可能正在 voice/RTP 打包。