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. 人物关系 人脸识别_原因解密:格里兹曼宣布终止与华为合作,不只是因为人脸识别系统...
  2. 《评人工智能如何走向新阶段》后记(再续7)
  3. LNMP架构php-fpm相关配置
  4. 基于htmlparser实现网页内容解析
  5. apache prefork和worker
  6. 2018.07.30 巴别时代
  7. SAP Fiori Elements - how to set breakpoint to get converted xml view parsed by f
  8. mysql+rsyslog+loganalyzer的部署方案
  9. java 判断数字变化增减_java String 强化操作 判断数字 字符串转阿拉伯数字,相似度等等...
  10. C++游戏入门书籍推荐
  11. python-格化输出format方法
  12. mysql三表联合更新_mysql三表连接update
  13. 国家自然科学基金申请书写作攻略
  14. python将字典写入txt文件_将一个字典写入txt文件并将其读回来?
  15. SwiftUI iOS 精品开源之 具有货币转换功能的iOS计算器 网络后端汇率API (教程含源码)
  16. Spring-Boot开发者工具:自动重启、LiveReload、远程开发
  17. 如何批量将图片转换为 Word 文档
  18. 句法结构可视化工具(成分句法)
  19. 读书笔记 --- 组织结构设计
  20. Python实现Iris数据集(鸢尾花卉数据集)kmeans聚类

热门文章

  1. Spring Cloud系列之Commons - 1. 背景与基础知识准备
  2. sql如何查询表的第一条记录和最后一条记录
  3. 支付宝三方支付——沙箱环境模拟支付
  4. VS code+Marp实现Markdown转换成PPT格式
  5. 利用不同的占空比来调整数码管和LED的亮度,仅用1个定时器
  6. 前端实习日记——前端开发环境配置清单
  7. [前端日志框架]---开篇
  8. matlab顶层,购房攻略:史上最完美的顶楼选房攻略,没有之一
  9. 1295. Crazy Notions
  10. FOF常用的七种投资策略全解析