安卓demo-wifi点对点视频实时传输实例及原理

最近学习了视频硬编解码,觉得这种视频传输的方式还挺不错的,写了个使用wifi点对点投屏传输demo,支持h264、h265。分享一下。

代码

https://gitee.com/flying-guy/practical-demo/tree/master/VideoTest

流程

视频传输流程图

具体步骤如下:

1.申请权限

android.permission.CAMERA
android.permission.INTERNET
android.permission.RECORD_AUDIO

2.创建工具类

权限申请成功以后,创建视频截屏工具MediaProjection、视频传输工具类SocketLive

3.开启服务端

开启服务端WebSocketServer等待客户端连接

4.解码准备

创建解码工具类CodeLiveH264、配置MediaFormat、MediaCodec、将MediaCodec与MediaProjection绑定,使用MediaProjection采集好的数据进行解码,因为MediaProjection的数据都是采集好的,不用再编码了,直接拿来解码。

5.解码

需要将通过MediaCodec拿到的数据解码成一帧一帧的,传到客户端后,客户端能够解码播放。

6.服务端发送数据

视频解码播放需要sps(基础配置帧)和pps(全量配置帧),编码好的数据只有视频开始时才会有一帧sps、pps,因此需要将sps和pps缓存,等客户端连上的时候先把spspps配置帧添加到I帧后面,这样每一个I帧都是完整的可以独立解析显示出画面的帧了,通过WebSocket发过去,再发后续解码出来的视频帧。

7.客户端接受数据

客户端通过WebSocketClient的onMessage接口接收到ByteBuffer

8.客户端展示数据

将接受到的数据渲染到Surface上进行展示,服务端一直发送数据,客户端就会一直显示。

效果图

点对点视频传输效果图

效果图就是可以在客户端看到服务端的屏幕。

  • 如果将服务端的投屏的SurfaceView改成后置摄像头预览画面,这样就是传的服务端录的像啦
  • 也可以直接把MediaProjection采集数据改成使用摄像头采集数据然后再自己编码一下,效果一样的。
  • 把服务端和客户端的MediaCodec的类型配置成"video/hevc",配置帧改成VPS就可以实现h265高清录屏传输了。
  • 使用WebSocketClient接受/发送数据,使用WebSocketServer发送/接受数据就可以完成视频通话啦,如下图所示。

点对点双向视频传输效果图

核心代码

/**
 * 处理帧
 * sps+pps I帧
 *
 * @param byteBuffer
 * @param bufferInfo
 */
private void dealFrame(ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) {
    int offset = 4;
    if (byteBuffer.get(2) == 0x01) {
        offset = 3;
    }
    int type = (byteBuffer.get(offset) & 0x1F);
    if (type == NAL_SPS) { //收到了sps,sps只会输出一份
        Log.d(TAG, "dealFrame: sps & pps");
        sps_pps_buf = new byte[bufferInfo.size];
        byteBuffer.get(sps_pps_buf); //将sps和pps缓存到数组
    } else if (type == NAL_I) { //I帧
        Log.d(TAG, "dealFrame: I frame");
        final byte[] bytes = new byte[bufferInfo.size];
        byteBuffer.get(bytes); //45459 I帧数据
        byte[] newBuf = new byte[sps_pps_buf.length + bytes.length];
        //将sps和pps拷贝到容器newBuf前面,从0开始,拷贝sps_pps_buf.length这么长
        System.arraycopy(sps_pps_buf, 0, newBuf, 0, sps_pps_buf.length);
        //将bytes I帧数据拷贝到新容器newBuf后面,从sps_pps_buf.length开始,拷贝bytes.length这么长
        System.arraycopy(bytes,0,newBuf,sps_pps_buf.length,bytes.length);
        socketLive.sendData(newBuf);
    } else {
        final byte[] bytes = new byte[bufferInfo.size];
        byteBuffer.get(bytes);
        socketLive.sendData(bytes);
        Log.d(TAG, "dealFrame: 视频数据 " + Arrays.toString(bytes));
    }
}

服务端解码核心代码

视频解码播放需要sps(基础配置帧)和pps(全量配置帧),配置帧里面携带了该视频的宽高、显示格式、纠错单元、优先级顺序、策略信息、编码等级等基础信息,相当于该视频的个人信息。编码好的数据只有视频开始时才会有一帧sps、pps,因此需要将sps和pps缓存,把配置帧怼到I帧前面才能让客户端解码成功.

原理

视频数据图

一个完整的NALU单元结构图

抓个编码好的视频数据看看,视频的开始就是一个spspps配置帧,且只出现一次。(如果是yuv原始数据,就会有多个,因为怕spspps丢失导致视频播放失败,不过使用yuv数据也需要自己记忆spspps,不然丢失以后需要等到下一个spspps的出现视频才能恢复播放)

NALU Type

包含

作用

sps(全量配置帧)

67

纠错单元

优先级顺序

策略信息

编码等级

 记录了编码的 Profile、level、图像宽高等

pps(基础配置帧)

68

显示格式(yuv420、421...)

视频宽高

每一帧编码后数据所依赖的参数保存于 PPS 中

I帧

65

视频数据

每一帧编码好的数据

参考文章:NALU

优点

代码简单、使用方便、能完成基础的视频传输工作、与其他视频传输协议原理相似、好理解、核心数据被编码成byte

待改进的地方

功能简单、只适用于安卓端点对点、没有做安卓版本兼容、没有声音(因为声音是ADTS,待补充)、wifi问题(连着几分钟自己就断了、距离远连不上),可以重连,不影响客户端观看效果

视频传输建议

若功能复杂,可使用RTMP协议,webRTC进行视频传输,OBS进行录屏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值