【问题标题】:Unable to send video as rtp stream from android无法从 android 将视频作为 rtp 流发送
【发布时间】:2015-01-23 06:26:14
【问题描述】:

我正在尝试制作 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://@: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 个字节来修复我的代码,因为 stackoverflow 中有人说在 AVC 中我们需要删除第一个 4 个字节。还检查了我的 RTP 标头两次,但没有运气...

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

【问题讨论】:

标签: android android-camera rtp android-mediacodec


【解决方案1】:

您不能只添加 RTP 标头,您还需要重新格式化编码缓冲区以适合 H264 RTP 有效负载格式的一个或多个固定长度 RTP 数据包(也称为“packetize”),请参阅RFC 6184完整的规范。

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

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

【讨论】:

    【解决方案2】:

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

    找一个lib 为你做这件事。

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

    我之所以提到它,是因为我前段时间在 SIP/VOIP 上下文中在 android 上执行 SDP/RTP 并发现它是可服务/可行的。

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

    一般来说,我相信您会扩展某种“会话”,它只是包装/挂钩您的视频频道/流,他的示例可能正在执行语音/RTP 打包。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-20
      • 2017-03-25
      • 1970-01-01
      • 2016-04-03
      • 2012-05-20
      • 2017-09-25
      相关资源
      最近更新 更多