如何实现RTMP或RTSP播放端回调YUV/RGB数据?

今天某乎收到个问题推荐,如何实现RTSP回调YUV数据,用于二次处理?

正好前些年我们做RTSP和RTMP直播播放的时候,实现过相关的需求,本文就以Android为例,大概说说具体实现吧。

先说回调yuv或rgb这块意义吧,不管是RTSP还是RTMP直播播放模块,解码后的yuv/rgb数据,可以实现比如快照(编码保存png或jpeg)、回调给第三方用于比如视频分析、亦或比如回调给Unity,实现Unity平台下的绘制。

为了图文并茂,让大家有个基本的认识,先上张图,demo展示的是本地播放的同时,可把yuv或rgb回上来,供上层做二次处理:

我们把协议栈这块处理,放到JNI下,播放之前,设置回调:

libPlayer.SmartPlayerSetExternalRender(playerHandle, new I420ExternalRender());

I420ExternalRender()具体实现:

/** SmartPlayer.java* SmartPlayer* * Github: https://github.com/daniulive/SmarterStreaming* * Created by DaniuLive on 2015/09/26.*/class I420ExternalRender implements NTExternalRender {// public static final int NT_FRAME_FORMAT_RGBA = 1;// public static final int NT_FRAME_FORMAT_ABGR = 2;// public static final int NT_FRAME_FORMAT_I420 = 3;private int width_ = 0;private int height_ = 0;private int y_row_bytes_ = 0;private int u_row_bytes_ = 0;private int v_row_bytes_ = 0;private ByteBuffer y_buffer_ = null;private ByteBuffer u_buffer_ = null;private ByteBuffer v_buffer_ = null;@Overridepublic int getNTFrameFormat() {Log.i(TAG, "I420ExternalRender::getNTFrameFormat return "+ NT_FRAME_FORMAT_I420);return NT_FRAME_FORMAT_I420;}@Overridepublic void onNTFrameSizeChanged(int width, int height) {width_ = width;height_ = height;y_row_bytes_ = (width_ + 15) & (~15);u_row_bytes_ = ((width_ + 1) / 2 + 15) & (~15);v_row_bytes_ = ((width_ + 1) / 2 + 15) & (~15);y_buffer_ = ByteBuffer.allocateDirect(y_row_bytes_ * height_);u_buffer_ = ByteBuffer.allocateDirect(u_row_bytes_* ((height_ + 1) / 2));v_buffer_ = ByteBuffer.allocateDirect(v_row_bytes_* ((height_ + 1) / 2));Log.i(TAG, "I420ExternalRender::onNTFrameSizeChanged width_="+ width_ + " height_=" + height_ + " y_row_bytes_="+ y_row_bytes_ + " u_row_bytes_=" + u_row_bytes_+ " v_row_bytes_=" + v_row_bytes_);}@Overridepublic ByteBuffer getNTPlaneByteBuffer(int index) {if (index == 0) {return y_buffer_;} else if (index == 1) {return u_buffer_;} else if (index == 2) {return v_buffer_;} else {Log.e(TAG, "I420ExternalRender::getNTPlaneByteBuffer index error:" + index);return null;}}@Overridepublic int getNTPlanePerRowBytes(int index) {if (index == 0) {return y_row_bytes_;} else if (index == 1) {return u_row_bytes_;} else if (index == 2) {return v_row_bytes_;} else {Log.e(TAG, "I420ExternalRender::getNTPlanePerRowBytes index error:" + index);return 0;}}public void onNTRenderFrame(int width, int height, long timestamp){if ( y_buffer_ == null )return;if ( u_buffer_ == null )return;if ( v_buffer_ == null )return;y_buffer_.rewind();u_buffer_.rewind();v_buffer_.rewind();/*if ( !is_saved_image ){is_saved_image = true;int y_len = y_row_bytes_*height_;int u_len = u_row_bytes_*((height_+1)/2);int v_len = v_row_bytes_*((height_+1)/2);int data_len = y_len + (y_row_bytes_*((height_+1)/2));byte[] nv21_data = new byte[data_len];byte[] u_data = new byte[u_len];byte[] v_data = new byte[v_len];y_buffer_.get(nv21_data, 0, y_len);u_buffer_.get(u_data, 0, u_len);v_buffer_.get(v_data, 0, v_len);int[] strides = new int[2];strides[0] = y_row_bytes_;strides[1] = y_row_bytes_;int loop_row_c = ((height_+1)/2);int loop_c = ((width_+1)/2);int dst_row = y_len;int src_v_row = 0;int src_u_row = 0;for ( int i = 0; i < loop_row_c; ++i){int dst_pos = dst_row;for ( int j = 0; j <loop_c; ++j ){nv21_data[dst_pos++] = v_data[src_v_row + j];                   nv21_data[dst_pos++] = u_data[src_u_row + j];}dst_row   += y_row_bytes_;src_v_row += v_row_bytes_;src_u_row += u_row_bytes_;}String imagePath = "/sdcard" + "/" + "testonv21" + ".jpeg";Log.e(TAG, "I420ExternalRender::begin test save iamge++ image_path:" + imagePath);try{File file = new File(imagePath);FileOutputStream image_os = new FileOutputStream(file);   YuvImage image = new YuvImage(nv21_data, ImageFormat.NV21, width_, height_, strides);  image.compressToJpeg(new android.graphics.Rect(0, 0, width_, height_), 50, image_os);  image_os.flush();  image_os.close();}catch(IOException e){e.printStackTrace();}Log.e(TAG, "I420ExternalRender::begin test save iamge--");}*/Log.i(TAG, "I420ExternalRender::onNTRenderFrame w=" + width + " h=" + height + " timestamp=" + timestamp);// copy buffer// test// byte[] test_buffer = new byte[16];// y_buffer_.get(test_buffer);// Log.i(TAG, "I420ExternalRender::onNTRenderFrame y data:" + bytesToHexString(test_buffer));// u_buffer_.get(test_buffer);// Log.i(TAG, "I420ExternalRender::onNTRenderFrame u data:" + bytesToHexString(test_buffer));// v_buffer_.get(test_buffer);// Log.i(TAG, "I420ExternalRender::onNTRenderFrame v data:" + bytesToHexString(test_buffer));}}

为了验证回上来的数据是否正常,我们加了保存jpeg文件的代码。

当然,回调yuv或rgb,可以做的更精细,比如我们windows的RTMP或RTSP播放器,回调数据,可以指定分辨率(比如缩放)和frame类型:

/*设置视频回调, 吐视频数据出来, 可以指定吐出来的视频宽高*handle: 播放句柄*scale_width:缩放宽度(必须是偶数,建议是 16 的倍数)*scale_height:缩放高度(必须是偶数*scale_filter_mode: 缩放质量, 0 的话 SDK 将使用默认值, 目前可设置范围为[1, 3], 值越大 缩放质量越好,但越耗性能*frame_format: 只能是NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, NT_SP_E_VIDEO_FRAME_FROMAT_I420成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetVideoFrameCallBackV2)(NT_HANDLE handle,NT_INT32 scale_width, NT_INT32 scale_height,NT_INT32 scale_filter_mode, NT_INT32 frame_format,NT_PVOID call_back_data, SP_SDKVideoFrameCallBack call_back);

相关视频帧图像格式和帧结构:

//定义视频帧图像格式
typedef enum _NT_SP_E_VIDEO_FRAME_FORMAT
{NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 = 1, // 32位的rgb格式, r, g, b各占8, 另外一个字节保留, 内存字节格式为: bb gg rr xx, 主要是和windows位图匹配, 在小端模式下,按DWORD类型操作,最高位是xx, 依次是rr, gg, bbNT_SP_E_VIDEO_FRAME_FORMAT_ARGB  = 2, // 32位的argb格式,内存字节格式是: bb gg rr aa 这种类型,和windows位图匹配NT_SP_E_VIDEO_FRAME_FROMAT_I420  = 3, // YUV420格式, 三个分量保存在三个面上
} NT_SP_E_VIDEO_FRAME_FORMAT;// 定义视频帧结构.
typedef struct _NT_SP_VideoFrame
{NT_INT32  format_;  // 图像格式, 请参考NT_SP_E_VIDEO_FRAME_FORMATNT_INT32  width_;   // 图像宽NT_INT32  height_;  // 图像高NT_UINT64 timestamp_; // 时间戳, 一般是0,不使用, 以ms为单位的// 具体的图像数据, argb和rgb32只用第一个, I420用前三个NT_UINT8* plane0_;NT_UINT8* plane1_;NT_UINT8* plane2_;NT_UINT8* plane3_;// 每一个平面的每一行的字节数,对于argb和rgb32,为了保持和windows位图兼容,必须是width_*4// 对于I420, stride0_ 是y的步长, stride1_ 是u的步长, stride2_ 是v的步长,NT_INT32  stride0_;NT_INT32  stride1_;NT_INT32  stride2_;NT_INT32  stride3_;} NT_SP_VideoFrame;

感兴趣的开发者可以酌情参考,实现自己的业务逻辑。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552971.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win7系统打印机删除不掉的解决方法

在Win7系统下&#xff0c;如果想要把安装的打印机删除&#xff0c;通常会在设备和打印机里删除打印机图标&#xff0c;但是一些用户反馈&#xff0c;用户在删除了打印机之后&#xff0c;只要一刷新打印机又会重新出现&#xff0c;感觉是删除不了&#xff0c;那么Win7系统打印机…

GB/T28181-2016传输要求和Android平台设备接入技术实现

相关协议规范 GB/T28181-2016公共安全视频监控联网系统 信息传输、交换、控制技术要求相关的传输要求如下&#xff1a; 5.1 网络传输协议要求 联网系统网络层应支持IP协议,传输层应支持 TCP和 UDP协议。 5.2 媒体传输协议要求 视音频流在基于IP的网络上传输时应支持 RTP/R…

无法打开Win11系统小组件怎么办

Win11操作系统也推出一段时间了&#xff0c;不少小伙都有下载体验&#xff0c;Win11与之前的Win10比较起来改动非常大&#xff0c;小组件是Win11系统中全新添加的功能&#xff0c;他类似于Win10最新的加入的兴趣模块&#xff0c;但是有朋友遇到了小组件无法打开的情况这是怎么回…

GB28181控制、传输流程和协议接口之注册|注销和技术实现

注册和注销基本要求 SIP客户端、网关、SIP设备、联网系统等 SIP代理(SIP UA)使用IETFRFC3261中定义的方法 15 GB/T28181—2016Register进行注册和注销。 注册和注销时应进行认证&#xff0c;认证方式应支持数字摘要认证方式&#xff0c;高安全级别的宜支持数字证书的认证方式&…

Edge浏览器网页怎么收藏 Edge浏览器网页收藏图文教程

Edge浏览器是微软最新推出的网页浏览工具。而现在有很多Edge浏览器新用户可能对它不是很了解&#xff0c;在使用过程中会出现小问题。今天小编就为大家解答Edge浏览器网页怎么收藏&#xff0c;一起来看教程吧! Edge浏览器网页收藏图文教程 一、怎么收藏网页? 1、打开Edge浏…

GB28181状态信息报送解读及Android端国标设备接入技术实现

今天主要聊聊GB/T28181状态信息报送这块&#xff0c;先回顾下协议规范相关细节&#xff0c;然后再针对代码实现&#xff0c;做个简单的说明。 状态消息报送基本要求 当源设备(包括网关、SIP设备、SIP客户端或联网系统)发现工作异常时,应立即向本 SIP监控域 的SIP服务器发送状…

IE浏览器怎么设置兼容性 添加兼容站点方法

IE浏览器是我们工作和生活中应用最广泛的浏览器&#xff0c;其安全、便捷是它的特点之一。不过这个浏览器在浏览部分网页的时候&#xff0c;会出现图片不刷新的情况&#xff0c;今天小编就带来了解决方法&#xff0c;告诉大家IE浏览器怎么设置兼容性。 1、首先点击IE浏览器右上…

GB28181设备接入端如何实现校时?

在探讨这个问题之前&#xff0c;我们先看看GB/T28181-2016官方文档怎么说的&#xff0c;9.10.1章节校时基本要求提到&#xff1a; 联网内设备支持基于SIP方式或 NTP方式的网络校时功能&#xff0c;标准时间为北京时间。 SIP方式校时见本节具体描述&#xff1b;NTP(见IETFRFC2…

Win7运行窗口的打开方法

Win7系统中运行窗口可谓是最常用的功能之一&#xff0c;可以帮助我们快速执行程序&#xff0c;在系统有问题时经常运用其协助解决。有用户问到运行窗口在哪?如何打开?找不到怎么办?其实方法不难&#xff0c;下面小编就跟大家详细介绍一下Win7运行窗口的打开方法。 Win7运行…

如何在Unity下采集音视频实现轻量级RTSP服务(类似于IPC)

好多开发者在做虚拟仿真、VR教育等场景的时候&#xff0c;遇到个问题&#xff0c;想把头显里面的画面在内网环境下低延迟的同步出来&#xff0c;又不想单独部署流媒体服务器。为此&#xff0c;我们在Unity下&#xff0c;添加了轻量级RTSP服务模块&#xff0c;通过头显端启动个轻…

【Datawhale 大模型基础】第十一章 环境影响

第十一章 环境影响 This blog is based on datawhale files and a paper. The initial consideration revolves around the potential positive or negative direct impact on the environment. Other transformative technological advancements, like the metaverse, are li…

Win7笔记本如何调整屏幕亮度

长期使用电脑的时候会因为视觉疲劳感觉屏幕亮度过高&#xff0c;需要对其调整来缓解。PC用户方法很简单&#xff0c;只需在显示器上点击按钮进行调整即可&#xff0c;那么Win7笔记本如何调整屏幕亮度?其实Win7系统本身有考虑到这个问题&#xff0c;在系统中有专门针对此问题的…

Android GB28181接入端实时位置订阅和上报之-如何获取当前经纬度

我们在做Android平台GB28181的时候&#xff0c;其中实时位置(MobilePosition)订阅和上报这块&#xff0c;涉及到实时经纬度的获取&#xff0c;特别是执法记录、车载系统的那个等场景&#xff0c;几乎就是标配。 今天主要是分享一段实时获取位置的代码&#xff1a; /** Camera…

Win7系统防火墙设置不了怎么办

防火墙是Windows系统中一项重要的防护措施&#xff0c;可以帮助你的电脑阻挡病毒和恶意软件&#xff0c;确保系统安全。近来有用户反馈win7系统的防火墙无法设置&#xff0c;Win7系统防火墙设置不了怎么办?无法设置说明系统设置被更改&#xff0c;防火墙功能应该是被禁用了&am…

如何实现Android平台GB28181设备对接Camera2数据

技术背景 在写如何实现Android平台GB28181设备对接Camera2数据说明之前&#xff0c;我在前两年的blog就有针对camera2的RTMP直播推送模块做过技术分享&#xff1a; 在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用…

Win7性能信息和工具在哪打开

在Win7系统中&#xff0c;有一个性能信息和工具&#xff0c;我们可以通过这个功能中的高级工具&#xff0c;查看到关于系统的关键信息&#xff0c;全面了解系统的运行状况&#xff0c;不过&#xff0c;对于不熟悉电脑的用户来讲&#xff0c;可能连性能信息和工具在哪都不清楚&a…

Android平台实现mp4文件实时推送RTMP|轻量级RTSP服务|GB28181平台

好多开发者有这样的诉求&#xff0c;想把本地录制的MP4文件&#xff0c;以实时流数据的形式&#xff0c;推送到RTMP服务器&#xff0c;注入轻量级RTSP服务&#xff0c;或者对接到GB28181平台&#xff0c;这块前几年我们就有对接。 本次以MediaExtractor为例&#xff0c;先利用…

Win10系统字体太小的调整设置教程

Win7系统与Win10系统还是差别挺多的&#xff0c;一些小伙伴们刚开始使用Win10系统对很多功能都了解。有的小伙伴就遇到了不适应字体的大小&#xff0c;但是又不知道Win10字体太小怎么调整&#xff0c;下面告诉大家Win10系统字体太小的调整设置教程。 Win10系统字体太小的调整设…

Android平台GB28181设备接入端本地SIP端口被占用或屏蔽怎么办?

好多开发者或厂商&#xff0c;对Android平台GB28181接入模块的定位&#xff0c;大多是IPC国标流程打通模拟&#xff0c;基于这个目的&#xff0c;很难按照标准SPEC规范实现Android平台GB28181设备接入&#xff0c;我们在跟第三方国标平台厂商对接时发现&#xff0c;部分公司&am…

Android平台GB28181设备接入端如何实现本地录像?

实现Android平台GB28181设备接入的时候&#xff0c;有个功能点不可避免&#xff0c;那就是本地录像&#xff0c;实际上&#xff0c;在实现GB28181设备接入模块之前&#xff0c;我们前些年做RTMP推送和轻量级RTSP服务的时候&#xff0c;早已经实现了本地录像功能。 本地录像功能…