完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
为什么使用TextureView?
TextureView 和 SurfaceView 的区别在哪? 怎样通过实现SurfaceView的函数来创建SurfaceView呢? |
|
相关推荐
1个回答
|
|
为何使用TextureView
原因是公司导师要求,原本RkVideoPlayer是用的SurfaceView,现在给我的任务就是使用TextureView来替换视频SurfaceView。原因是播放之前使用SurfaceView,在播放中按电源键暂停,再打开,会暂停,不会继续,所以替换成TextureView是不是能继续播放。那么就先看看TextureView和SurfaceView的区别吧。 SurfaceView 继承于 android.view.View,其他的TextureView,此时可以与独立线程的中绘制和渲染,在专用的GPU线程中放大和渲染的渲染。 SurfaceView,不能添加动画、覆盖面视图;2.不能同时覆盖; 而TextureViewText类是一般的View,动画使用那样的被开启、还有必须的window里也能加入了硬件加速,。 就比较开始这俩研究的了,以后有什么区别。 表面视图 公共类 VideoDisplayView 扩展 SurfaceView 实现 VideoPlayerControl、OnClickListener 本来是让VideoDisplayView继承SurfaceView的同时实现VideoPlayerControl、OnClickListener接口,再通过实现SurfaceView的几个函数来SurfaceView的创建。 SurfaceHolder.Callback mSHCallback = 新 SurfaceHolder.Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { LOG("<----------surfaceChanged------> format=" + format + "w=" + w + " h=" + h); mSurfaceWidth = w; mSurfaceHeight = h; if (mMediaPlayer != null && mIsPrepared && mVideoWidth == w && mVideoHeight == h) { LOG("CASE 5 EXE"); 如果(mSetScreebSizeClicked == true){ 返回; } } 否则返回; } 公共无效surfaceCreated(SurfaceHolder持有人){ LOG("<--------surfaceCreate--------->"); mSurfaceHolder = 持有人; 打开视频(); } 公共无效surfaceDestroyed(SurfaceHolder持有人){ LOG("<------------surfaceDestoryed-------->"); // 从这里返回后,我们不能再使用表面了 mSurfaceHolder = null; if (mVideoController != null) mVideoController.hide(); 如果(mMediaPlayer!= null){ mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; } } }; 纹理视图 现在正题,先修改进入VideoDisable的继承,继承于TextureView,继承于TextureView,再通过实现Texture.SurfaceTextureListener接口来完成的问题。这里实现小视频播放,TextureView得在sdk14以上才行,一开始我把AndroidManifest.xml里面的问题把minSdkVersion删掉了,导致一直报错看,错误的信息才知道,TextureView是要在Android4.0以上的android,所以得设置好minSdkVersion,这样的情况,心嘻嘻。 TextureViewSurSurfaceTextureListener 就是监听最重要的界面。下面开始讲函数 先来看看 SurfaceTextureListener接口里的源头是怎么写的 公共静态接口 SurfaceTextureListener { /** * 当 {@link TextureView} 的 SurfaceTexture 可以使用时调用。 * * @param surface 返回的表面 * {@link android.view.TextureView#getSurfaceTexture()} * @param width 曲面的宽度 * @param height 表面的高度 */ public void onSurfaceTextureAvailable(SurfaceTexture 表面,int 宽度,int 高度); /** * 当 {@link SurfaceTexture} 的缓冲区大小更改时调用。 * * @param surface 返回的表面 * {@link android.view.TextureView#getSurfaceTexture()} * @param width 曲面的新宽度 * @param height 曲面的新高度 */ 公共无效 onSurfaceTextureSizeChanged(SurfaceTexture 表面,int 宽度,int 高度); /** * 当指定的 {@link SurfaceTexture} 即将被销毁时调用。 * 如果返回true,则在此方法之后不应在表面纹理内部进行渲染 * 被调用。如果返回 false,客户端需要调用 {@link SurfaceTexture#release()}。 * 大多数应用程序应该返回 true。 * * @param surface 即将被销毁的表面 */ 公共布尔 onSurfaceTextureDestroyed(SurfaceTexture 表面); /** * 当指定的 {@link SurfaceTexture} 通过更新时调用 * {@link SurfaceTexture#updateTexImage()}。 * * @param surface 刚刚更新的表面 */ 公共无效 onSurfaceTextureUpdated(SurfaceTexture 表面); } 有这样一个正确的函数,其中最重要的函数是什么时候执行的呢? 再设置一个东西,设置SurfaceTexture),在初始化数据的ururVideoDisplayView的时候,SurfaceText准备当源这样注册的时候,可以在SurfaceTexter上播放,然后在SurfaceTexture上播放一个,用SurfaceTexture来关联媒体作为播放的(作为播放的)。 SurfaceTexture 作为通道,把从数据源(MediaPlayer)中获取到的硬件显示的图像帧数据转为外部视频作为外部视频,作为外部视图的数据查看TextureVeiw中的一个加速层来,实现播放功能。 说到这个MediaPlayer,也讲他的播放器吧。 于他的,他是Android提名我的播放器,用他可以当地理解或者说他在线播放的最重要的几种状态,下面用老图来展示吧。 图包含了MediaPlayer的状态,也就是他的生命周期,因为这个重点不在,就那么细的几句。当我把我的印象全部讲出来的时候,闲置状态在MediaPlayernew创建进入,mMediaPlayer = new MediaPlayer(),在这个状态下setDataSource()方法设置通知先初始化,然后执行初始化,调用状态()调用preparedA,通过OnPreparedListener.onPrepared()状态状态为方法进入PreParing()状态准备好了,此时状态就可以start()进行播放了。 完onSurfaceAvailable,再讲SurfaceTextureDestroyed,就像字面的文字一样 当指定的 {@link SurfaceTexture} 即将被销毁时调用。 * 如果返回true,则在此方法之后不应在表面纹理内部进行渲染 * 被调用。如果返回 false,客户端需要调用 {@link SurfaceTexture#release()}。 * 大多数应用程序应该返回 true。 都是返回true,当返回为false时,得调用释放()方法释放。 遇到的问题 几个搞清楚了View.SurfaceListener的表面代码TextureText之后,就可以替换了。在替换了,遇到了问题。 1.选择好播放时,进入播放器但未播放视频,或者点击窗口播放状态,再点击退出,或者小屏幕显示屏时则开始播放。 一开始遇到这个bug让我很不解,为什么一开始不播放,反而点击退出时会开始播放呢? 我观察开始期间的日志,发现打印运行所在的地方。当点击说明进入播放器的时候,日志了openVideo(),进入了播放的方法没错,但是底下的方法的日志一概没有,此时,而当点击返回,重新打印时输入 openVideo()(说明时显示问题,但在 SurfaceText 上显示了可用的方法),当返回底下的方法正常执行,理解开始播放。 那出问题在哪呢? 仔细阅读了一段代码,发现问题了,在openVideo方法中有这么一段代码。 if (mUri == null || mSurfaceHolder == null) { // 还没有准备好播放,稍后再试 返回; } 判断mUri和mSurfaceHolder是否为空,为空直接返回。那不是就是因为mSurfaceHolder为空直接返回所以不猜视频呢? ,我又在SurfaceTexture上可用的方法,其中有一行这样的代码。 mSurfaceHolder = 表面; 说明方法要在判断方法时(SurfaceTextureAvailable) 中是否需要打开SurfaceHolder 是否为然后空返回,则判断。 onSurfaceTextureAvailable: @覆盖 公共无效 onSurfaceTextureAvailable(SurfaceTexture 表面,int 宽度,int 高度){ LOG("<------------SurfaceTexture 可用-------------->"); 如果(mSurfaceHolder == null){ mSurfaceHolder = 表面; 打开视频(); }别的{ mMediaPlayer.setSurface(new Surface(mSurfaceHolder)); } } 此时再重新测试,发现播放后会开始打印播放了,再查看log,果然点击此时进入了openVideo()方法后<-----------SurfaceTexture is Available--- ------------>,说明进入了执行,执行了mSurfaceolder = surface,再执行如下代码: mMediaPlayer = new MediaPlayer(); LOG("mMediaPlayer = " + mMediaPlayer); mIsPrepared = 假; mMediaPlayer.setOnPreparedListener(mPreparedListener); LOG("在 openVideo 中将持续时间重置为 -1"); mDuration = -1; mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener); mMediaPlayer.setSurface(new Surface(mSurfaceHolder)); mCurrentBufferPercentage = 0; LOG("-------------setDataSource-------------mUri:" + mUri); mMediaPlayer.setDataSource(mContext, mUri, null); //mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setScreenOnWhilePlaying(true); mMediaPlayer.prepareAsync(); 附加媒体控制器(); 播放问题解决! 2.还有一个问题,正常播放以后点击退出时,程序会报错 以上的经验,我应该推测出问题的方法。 也是,先观察日志,发现报了这么一个错误。 尝试在空对象引用上调用虚拟方法“void android.media.MediaPlayer.release()” 所以很有可能就是表面纹理销毁:针对问题,代码 @覆盖 公共布尔 onSurfaceTextureDestroyed(SurfaceTexture 表面) { mSurfaceHolder = null; mMediaPlayer.release(); 返回真; } 说明直接使用mMediaPlayer.release()不对,我找到之前的surfaceDestroyed方法,直接偷过来修改。 公共无效surfaceDestroyed(SurfaceHolder持有人){ LOG("<------------surfaceDestoryed-------->"); // 从这里返回后,我们不能再使用表面了 mSurfaceHolder = null; if (mVideoController != null) mVideoController.hide(); 如果(mMediaPlayer!= null){ mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; } /*if(mActivity != null ) { mActivity.Finish(); }*/ } @覆盖 公共布尔 onSurfaceTextureDestroyed(SurfaceTexture 表面) { LOG("<------------SurfaceTexture 被破坏-------------->"); mSurfaceHolder = null; if (mVideoController != null) mVideoController.hide(); 如果(mMediaPlayer!= null){ mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; } 返回真; } 再重新开始,嗨,变成! 点击播放,视频正常播放,点击退出,程序不再打印报错,查看日志,也是正常退出的信息。点击小窗口播放,也不再报错。 成功! 测试 再完整的播放播放,打开,打开,文件,正常播放,点击播放播放等,选择正常点击播放,正常点击播放等。 在播放中按电源键休眠,重新进入,视频不暂停,继续播放。 这下真正成功了,完成需求! 总结 修改代码其实比自己写的更,因为你得用其他方法写的逻辑,替换其中的代码又会关联到其他方法,一开始需要有把代码给看懂,这是第一个要发生的事情。 再结合一些现成的重要例子,调用方法调用的逻辑给弄弄,这回让我吃了大亏,一开始并没有把逻辑给理解了。 重新调试日志的时候,组织的日志基本能帮助把重要的结果放在一起,给组织带来清晰的测试。 要相信没有很重要的bug,只有你没弄懂的代码,有bug一定是你的代码写错了! |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
基于米尔瑞芯微RK3576核心板/开发板的人脸疲劳检测应用方案
1752 浏览 0 评论
1942 浏览 1 评论
1619 浏览 1 评论
2959 浏览 1 评论
3955 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-9 00:22 , Processed in 0.627070 second(s), Total 69, Slave 53 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号