推广 热搜: 收购ACF  石英加热管,  800  T型槽试验平台  求购ACF  深圳回收ACF  回收ACF  T型槽装配平台  求购日立ACF  T型槽地梁 

基于鸿蒙开发的应用(鸿蒙开源全场景应用开发)

   日期:2023-04-24     浏览:30    评论:0    
核心提示:背景 上期内容提到过,已开发的家庭合影美颜相机应用是同时基于鸿蒙和安卓设备的,我们将对其4个功能模块即视频编解码、视频渲染、通讯协议和美颜滤镜进行拆分讲解。上一期内容中,我们对视频编解码模块的实现原
背景

上期内容提到过,已开发的家庭合影美颜相机应用是同时基于鸿蒙和安卓设备的,我们将对其4个功能模块即视频编解码、视频渲染、通讯协议和美颜滤镜进行拆分讲解。上一期内容中,我们对视频编解码模块的实现原理进行了解析。本期将继续为大家讲解视频渲染模块,并解析鸿蒙视频渲染相关类之间的关系。相关代码已经开源到Gitee(https://gitee.com/isrc_ohos/cameraharmony),欢迎各位下载使用并提出宝贵意见!

家庭合影美颜相机应用效果回顾

先来带家一起回顾下上期内容讲解的家庭合影美颜相机应用。此应用能够将鸿蒙大屏拍摄的视频数据实时传输到安卓手机上;并在安卓端为其添加滤镜,再将处理后的视频数据传回到鸿蒙大屏进行渲染显示,从而实现鸿蒙大屏美颜拍照的功能,其流程可以参考图1,其数据流向图可以参考图2:

基于鸿蒙开发的应用(鸿蒙开源全场景应用开发)(1)

图1 家庭合影美颜相机应用的效果示意图

基于鸿蒙开发的应用(鸿蒙开源全场景应用开发)(2)

图2 美颜相机应用视频数据流向图

应用运行后的动态场景效果可以参考图3,图中下方竖屏显示的是安卓手机,上方横屏显示的是鸿蒙手机(由于实验环境缺少搭载鸿蒙系统的大屏设备,因此我们使用鸿蒙手机替代大屏设备模拟实验场景 ),其显示的是视频解码后渲染的效果。

基于鸿蒙开发的应用(鸿蒙开源全场景应用开发)(3)

图3 应用运行效果图

SurfaceProvider视频渲染解析

在鸿蒙中,SurfaceProvider是专门用于绘制图像视图的组件,作为基本组件之一,它通常被用于需要快速绘制图像的地方,如播放视频的情况。下面为大家讲解在完成视频编解码处理后,通过鸿蒙SurfaceProvider完成视频渲染显示的具体实现原理。共分为如下6个步骤:步骤1. 声明SurfaceProvider类对象。步骤2. 设置SurfaceProvider属性并添加在页面整体布局中。步骤3. 解码类VDDecoder继承 SurfaceOps.Callback接口类。步骤4. 获取SurfaceOps并设置回调。步骤5. 重写SurfaceCreated()方法,获取当前Surface。步骤6. 渲染视频数据。(1)声明SurfaceProvider类对象在进行视频渲染之前,需要声明用于渲染视频的SurfaceProvider类对象。

private SurfaceProvider surfaceview;// SurfaceProvider用于显示解码后的视频

(2)设置SurfaceProvider属性并添加在页面整体布局中实例化SurfaceProvider类对象并设置相关属性。先使用setWidth()和setHeight()方法设置大小;pinToZTop()方法使surfaceview置于屏幕布局最顶层显示。由于可能会出现待渲染视频数据本身是横屏而屏幕为竖屏显示,或待渲染视频数据本身是竖屏而屏幕为横屏显示等不匹配的情况,因此需要使用setRotation()方法调整屏幕参数,使得屏幕显示方向与视频数据方向相符,其中,屏幕参数0-180度为横屏显示,90-270度为竖屏显示,本应用中原始视频数据是横屏的所以此处需要将屏幕参数设置为180度。接着最主要的是,需要通过getSurfaceOps().get().addCallback()方法设置回调,这样可以通过回调将SurfaceProvider和设备相机相关联。

surfaceview1 = new SurfaceProvider(this); // 实例化类对象surfaceview1.setWidth(400); // 设置 SurfaceProvider 大小surfaceview1.setHeight(300);surfaceview1.getSurfaceOps().get().addCallback(callback);// 设置回调surfaceview1.pinToZTop(true);surfaceview1.setRotation(180); // 设置屏幕旋转角度

通过Layout的addComponent()方法将SurfaceProvider添加到整体布局中。

myLayout.addComponent(surfaceview); // 添加到布局中

(3)解码类VDDecoder继承SurfaceOps.Callback类SurfaceOps.Callback提供了SurfaceProvider被创建、销毁或者改变时的回调通知。由于进行视频渲染的阶段是在完成视频编解码处理之后,因此解码类VDDecoder需要继承SurfaceOps.Callback类,即为SurfaceOps提供一个回调接口。其中需要全局声明Surface和SurfaceOps类对象并重写SurfaceCreated()、SurfaceDestroyed()和SurfaceDestroyed()方法。

public class VDDecoder implements SurfaceOps.Callback { private SurfaceOps holder;// 全局声明SurfaceOps和SurfaceOps类对象 private Surface mSurface; ... @Override // 重写 SurfaceProvider被创建时的回调 public void surfaceCreated(SurfaceOps holder) { ...} @Override // 重写SurfaceProvider被改变时的回调 public void surfaceChanged(SurfaceOps holder, int format, int width, int height) { ...} @Override // 重写SurfaceProvider被销毁时的回调 public void surfaceDestroyed(SurfaceOps holder) { ...}}

(4)获取SurfaceOps并设置回调在实例化解码类对象时,将用于渲染编解码后视频的surfaceview作为参数传入。

vdDecoder = new VDDecoder(surfaceview);// 创建解码类对象,并使用surfaceview显示解码后的视频

在解码类VDDecoder构造函数中设置SurfaceProvider,调用SurfaceProvider类的getSurfaceOps().get()方法获取surfaceview的SurfaceOps;通过SurfaceOps类对象holder调用addCallback()方法设置回调;再调用setKeepScreenOn()方法,将参数设为true,来实现使屏幕一直显示不会自动关闭的效果。

public VDDecoder(SurfaceProvider playerView) { // 设置 SurfaceProvider,即使用 surfaceview播放解码后的视频 this.holder = surfaceview.getSurfaceOps().get(); holder.addCallback(this);// 设置回调 // 设置该组件让屏幕不会自动关闭 holder.setKeepScreenOn(true); ...}

(5)重写SurfaceCreated()方法,获取当前SurfacesurfaceCreated()和surfaceDestroyed()是渲染处理的边界,分别代表SurfaceProvider的创建和销毁,正式的渲染操作必须在SurfaceProvider被创建后才能进行。重写surfaceCreated()方法创建SurfaceProvider,将创建状态isSurfaceCreated变量设置为true,表示已创建;通过SurfaceOps类对象holder调用getSurface()方法获得当前Surface到类对象mSurface中,以便后续将视频数据通过mSurface渲染到界面上。

@Override // 重写 SurfaceProvider被创建时的回调public void surfaceCreated(SurfaceOps holder) { isSurfaceCreated = true; // 设置创建状态为已创建 mSurface = holder.getSurface(); // 获得当前Surface ...}

(6)渲染视频数据在编解码类的监听事件decoderlistener中,获取编解码后的数据准备渲染。由于得到的相机图像数据是逆时针旋转90度的,此时如果直接进行渲染,显示的也会是逆时针旋转的效果,因此为了得到正常的显示画面,需要对图像参数进行调整,调用rotateNV21()方法对视频画面进行顺时针旋转90度,并将旋转后的数据存放在byte数组rotate_bytes中。通过Surface类对象mSurface调用showRawImage()方法对旋转后的视频数据进行渲染。此方法第一个参数表示待渲染数据的byte数组;第二个表示待渲染数据的格式,由于此Demo中编解码的是摄像头直接获取的数据,所以格式是NV21即YUV420_SP;第三和第四个参数分别表示渲染画面的宽和高。

private Codec.ICodecListener decoderlistener = new Codec.ICodecListener() { // 用于监听解码器,获取解码完成后的数据 @Override public void onReadBuffer(ByteBuffer byteBuffer, BufferInfo bufferInfo, int i) { ... // 将解码后的 NV21(YUV420SP) 数据 bytes 顺时针旋转 90 度,并通过 Surface 显示 rotateNV21(bytes, rotate_bytes, 640, 480, 90);// 旋转后的数据用 rotate_bytes 存放 // 渲染旋转后的数据 rotate_bytes 通过mSurface显示出来,第二个参数是待渲染的数据格式即YUV420SP mSurface.showRawImage(rotate_bytes, Surface.PixelFormat.PIXEL_FORMAT_YCRCB_420_SP,640, 480); } ...};

之后运行并点击“开始编解码”按钮即可得到上述图1中展示的将编解码后的视频数据渲染在surfaceview中的效果。

Surface、SurfaceOps与SurfaceProvider的关系

经过上述讲解,相信大家已经能够在鸿蒙中正确使用SurfaceProvider来进行视频渲染了。熟悉安卓的读者可能已经发现,鸿蒙SurfaceProvider用法和安卓Surface用法有异曲同工之妙。为了方便理解,可以将鸿蒙中的SurfaceProvider、Surface和SurfaceOps分别与安卓中的SurfaceView、Surface、和SurfaceHolder对照查看,其原理类似。下面将为大家解析在鸿蒙中这三个视频渲染类之间的关系。

基于鸿蒙开发的应用(鸿蒙开源全场景应用开发)(4)

图4 SurfaceProvider、Surface、SurfaceOps关系示意图

1.Surface与SurfaceProvider关系

Surface与SurfaceProvider之间的关系如图2所示。在鸿蒙中,每个窗口会对应一个SurfaceProvider,每个Surface会对应一块屏幕缓冲区,而SurfaceProvider的作用是处理屏幕缓冲区中的视频数据,并使用该数据在屏幕上绘图。也就是说,Surfac负责对视频数据进行管理;eSurfaceProvider负责对视频数据进行展示,Surface需要通过SurfaceProvider才能展示其中的内容并控制视图的位置和尺寸。

2.SurfaceOps与SurfaceProvider关系

SurfaceOps是一个接口,其作用类似于一个关于Surface的监听器,能够访问SurfaceProvider对应的Surface并调用Surface中的相关方法。并通过三个回调方法,及时捕捉Surface的状态如创建、销毁或者改变。获取SurfaceOps的方式是:调用SurfaceProvider类中getSurfaceOps()方法,得到元素类型为SurfaceOps的Optional容器,再通过get()方法从容器中取出SurfaceOps类对象并返回。在成功调用并得到返回值之后,就可以通过返回的SurfaceOps类对象调用addCallback()方法为Surface设置回调:

void addCallback(SurfaceOps.Callback var1);// 设置SurfaceOps回调

图2中显示,在Surface与SurfaceProvider之间还存在一个SurfaceOps.Callback类,SurfaceOps的回调就是通过内部子接口SurfaceOps.Callback实现的,其有三个回调方法:

  • surfaceCreated():当SurfaceProvider发生结构性的变化如格式或大小改变时,调用此方法。
  • surfaceChanged():当SurfaceProvide被创建时,调用此方法。
  • surfaceDestroyed():当SurfaceProvider在要被销毁时,立即调用此方法。

public interface Callback { // 内部子接口CallBack void surfaceCreated(SurfaceOps var1);// SurfaceProvider被创建时 void surfaceChanged(SurfaceOps var1, int var2, int var3, int var4);// SurfaceProvider被改变时 void surfaceDestroyed(SurfaceOps var1);// SurfaceProvider被销毁时}

上面提到过SurfaceOps是一个接口,因此在实际使用之前,需要先重写上述三个回调方法,才能正常感知到SurfaceProvider的创建、改变或销毁。

项目贡献人

李珂 朱伟 郑森文 陈美汝

,
原文链接:http://www.souke.org/news/show-51977.html,转载和复制请保留此链接。
以上就是关于基于鸿蒙开发的应用(鸿蒙开源全场景应用开发)全部的内容,关注我们,带您了解更多相关内容。
 
标签: 鸿蒙 视频 方法
打赏
 
更多>同类资讯
0相关评论

推荐资讯
网站首页  |  VIP套餐介绍  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  SITEMAPS  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报