android recorder通过rtp发送h264视频数据给vlc播放

android player通过rtp协议接收h264视频数据播放

Android recorder通过rtp发送aac数据给vlc播放

Android player实现播放来自rtp连接的aac数据的player

目录

一、接收数据

二、分片组包

三、解码


一、接收数据

rtp包的组包与拆包已经由rtp 库完成,这里可以从rtp库的回调直接接收到原始发送的数据。

videoRtpWrapper.open(40018, 96, 90000);videoRtpWrapper.setCallback { data, len ->Log.d("dragon_video", "received video data $len")nalu.appended(data, len) { buffer, offset, size ->videoBufferQueue.put(buffer);videoBufferSizeQueue.put(size);}}

我们可以看到rtp payload指定的类型是96,96代表的就是h264视频数据类型。这里监听的是偶数端口40018,奇数端口留给rtcp使用。我们接收到的数据是nalu分片数据,我们还需要把分片数据组成完整的nalu数据。

二、分片组包

我们使用NaluData这个工具类进行组包操作,相应的他也提供了拆包方法给发送端使用。

class NaluData {private val F_MASK = 0b10000000.toByte()private val NRI_MASK = 0b01100000.toByte()private val TYPE_MASK = 0b00011111.toByte()private val START_MASK = 0b10000000.toByte()private val END_MASK = 0b01000000.toByte()private val RESERVE_MASK = 0b00100000.toByte()private val maxFragmentSize = 65535 - 1000private val data = ByteArray(1000 * 100)private var position = 0;fun appended(buffer: ByteArray, len: Int, sender: (ByteArray, Int, Int) -> Unit) {val type = buffer[0] and TYPE_MASK//fu indicator typeif (type == 0b11100.toByte()) {//fu indicator start/end flagval isStartFlag = (buffer[1] and START_MASK) == START_MASKval isEndFlag = (buffer[1] and END_MASK) == END_MASKwhen {isStartFlag -> {data[0] = 0data[1] = 0data[2] = 0data[3] = 1position = 4data[position] = (buffer[0] and (F_MASK or NRI_MASK)) or (buffer[1] and TYPE_MASK)position++System.arraycopy(buffer, 2, data, position, len - 2)position += (len - 2)}isEndFlag -> {System.arraycopy(buffer, 2, data, position, len - 2)position += (len - 2)sender.invoke(data, 0, position)}else -> {System.arraycopy(buffer, 2, data, position, len - 2)position += (len - 2)}}} else {data[0] = 0data[1] = 0data[2] = 0data[3] = 1position = 4System.arraycopy(buffer, 0, data, position, len)position += lensender.invoke(data, 0, position)}}......
}

接收到数据后第一步是判断是否是分片数据,因为当数据没有超过udp限制的大小的时候,我们可以直接发送原始的nalu数据,当数据超过限制的时候,我们才进行分片操作。
首先我们获取分片类型,根据是否能获取分片类型判断它是否是分片数据。先来看下分片数据的头部构成。

根据获取到的fu indicator type,fu indicator start/end flag进行组片操作。组片过程涉及到将fu indicator和fu  header转换成nalu header。根据插图中的描述,我们可以方便的获取转变后的nalu header。组片后要记得将0001头添加到nalu header前面。

三、解码

分片组装成nalu数据后可以直接提供给MediaCode进行解码,解码后的数据通过output surface渲染。
为了能够正确的解码数据,我们在初始化Mediacode阶段需要设置基本的视频信息。

 val videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 800)videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 750000)videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30)videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5)var currentTime = 0LvideoDecodeCodec = object : SurfaceDecodeCodec(videoFormat, outputSurface) {override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {val buffer = codec.getInputBuffer(index) ?: return;val data = videoBufferQueue.take()val size = videoBufferSizeQueue.take()val time = (System.currentTimeMillis() - currentTime) * 1000buffer.position(0)buffer.put(data, 0, size)codec.queueInputBuffer(index, 0, size, time, 0)}override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {}}

这里设置了视频的格式,分辨率,码率等信息。输入解码数据的时候需要指定数据的渲染时间,我简单的使用当前时间与解码器启动时间的差值来计算的。

abstract class SurfaceDecodeCodec(mediaFormat: MediaFormat, val outputSurface: Surface) : BaseCodec("SurfaceDecodeCodec", mediaFormat) {override fun onCreateMediaCodec(mediaFormat: MediaFormat): MediaCodec {val mediaCodecList = MediaCodecList(MediaCodecList.ALL_CODECS)val mediaCodecName = mediaCodecList.findDecoderForFormat(mediaFormat)return MediaCodec.createByCodecName(mediaCodecName)}override fun onConfigMediaCodec(mediaCodec: MediaCodec) {mediaCodec.configure(mediaFormat, outputSurface, null, 0)}override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) {codec.releaseOutputBuffer(index,true);}
}

由于我们需要把解码的数据显示在ui上,所以这里配置了outputSurface来输出解码后的数据。这个outputSurface就是SurfaceView构造的surface。

构造outputSurface的代码就比较简单了。

class MainActivity : AppCompatActivity() {var rtpPlayer: PlayerRtp? = null;var outputSurface: Surface? = null;override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)surfaceView3.holder.addCallback(object : SurfaceHolder.Callback {override fun surfaceCreated(holder: SurfaceHolder) {outputSurface = holder?.surface;rtpPlayer = PlayerRtp(outputSurface!!);}override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}override fun surfaceDestroyed(holder: SurfaceHolder) {rtpPlayer?.release();}})}
}

在SurfaceView的callback中直接可以获取surface,我们把这个surface提供给MediaCode渲染使用就可以了。

# Git
[https://github.com/mjlong123123/VideoPlayer](https://github.com/mjlong123123/VideoPlayer)

我的公众号已经开通,公众号会同步发布。
欢迎关注我的公众号

android player通过rtp协议接收h264视频数据播放相关推荐

  1. H264视频传输、编解码----RTP协议对H264数据帧拆包、打包、解包过程

    H264帧需要通过RTP协议进行传输,这其中就涉及到H264数据帧的封包.拆包和解包等过程. RTP协议格式 下面是 RFC 3550 中规定的 RTP 头的结构: 0 1 2 3 40 1 2 3 ...

  2. 基于Android4.2系统的H264视频数据的获取

    基于Android4.2系统的H264视频数据的获取 0引言 Andriod系统本身不支持H264视频原始压缩数据(即符合H264压缩标准,具有NAL头的裸数据)的实时获取,基本上都是利用MediaR ...

  3. 分析一段H264视频数据

    分析一段H264视频数据 Posted on 2007-05-31 09:42 vcommon 阅读(1968) 评论(8)  编辑 收藏 引用 分析 00 00 00 01 67 42 00 1E ...

  4. RTP协议封装H264/H265/AAC

    <RTSP实时音视频传输介绍> 目录 一.前言 二.RTP基本格式介绍 1.RTP 固定头 2.RTP 扩展头 3.RTP 载荷 三.RTP封装H264 1.封装包类型 四.RTP封装H2 ...

  5. 【FFMPEG】基于RTP的H264视频数据打包解包类

    最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包.解包的文档和代码.功夫不负有心人,找到不少有价值的文档和代码.参考这些资料,写了H264 RTP打包类.解包类,实现 ...

  6. RTP协议封装音视频媒体数据详解

    RTP协议对媒体数据(包括音频和视频)的封装是由指定的的协议文档规定. 1. RTP封装H.264视频编码数据 1.1 H.264 基本流的结构 H.264 的基本流(elementary strea ...

  7. 利用 android手机DLNA功能,实现手机视频无线播放到电脑、电视

    利用DLNA功能可实现个人PC.消费电器.移动设备在内的无线网络和有线网络的互联互通. DLNA可以支持的格式: Image:JPEG PNG, GIF, TIFF Audio:LPCM AAC, A ...

  8. Android如何回调编码后的音视频数据

    有开发者提到,在RTMP推送端的基础上,希望能回调编码后的音视频数据,便于开发者对接第三方系统,如GB28181. 为此,我们加了一下接口: 1. 设置音视频callback 对应接口: /*** S ...

  9. 小程序实现RTMP/HLS协议直播监控视频实时播放实现步骤

    小程序实现监控视频实时播放实现步骤 简述:RTMP协议直播视频,可使用小程序live-player组件实现视频播放,暂只针对国内主体特定类目的小程序开放,需要先通过类目审核,再在小程序管理后台,「开发 ...

最新文章

  1. Task和Activity相关(转)
  2. 利用PHP的Popen实现RRDTOOL作图的动态输出
  3. Fedora设置DVD为yum源
  4. 神经网络的反向传导到底是在干什么?
  5. python查看方法作用_python学习笔记1,新手小白也能看得懂
  6. multiple context container - entry point for tile click
  7. Oracle数据库DECODE函数的使用.
  8. 利用.NET Core类库System.Reflection.DispatchProxy实现简易Aop
  9. PHP根据指定url生成二维码图片
  10. Leetcode 14.最长公共前缀
  11. Unity有哪些让做项目事半功倍的插件值得推荐?
  12. 旷视孙剑团队提出AutoML神经架构搜索新方法:单路径One-Shot,更精确更省时
  13. Apache2.2整合PHP5.2
  14. 指定Pycharm在py3.6环境下启动
  15. html网页做一个打字小游戏,原生js实现的金山打字小游戏(实例代码详解)
  16. 孙玄:基于CAP模型设计企业级真正高可用的分布式锁
  17. 万兆网络传输速度测试_Intel万兆网卡的真实测试万兆速度的方法13718565365
  18. 获取微信视频号视频地址
  19. HTTPS TSL/SSL详解
  20. 网易云课堂个性化推荐实践与思考

热门文章

  1. Wannafly挑战赛3 珂学送分(期望dp)
  2. Wannafly挑战赛3-A-珂学送分(概率dp)
  3. overlapped
  4. Overlapped模型深入分析(原理篇)
  5. Suricata规则编写
  6. Windows修改中文用户名为英文
  7. 安全狗云原生安全能力全面亮相全球数字经济大会暨ISC互联网安全大会
  8. echarts树图实现搜索查询功能
  9. 大数据---大数据分析的道与术(笔记)
  10. SQL Server 2014查询提示“目录名称无效”