Android:SurfaceView中来自MediaCodec的裁剪视频 [英] Android: Crop video from MediaCodec in SurfaceView

查看:0
本文介绍了Android:SurfaceView中来自MediaCodec的裁剪视频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到的问题如下:

我有一块定制电路板,屏幕分辨率为900x500像素。主板运行Android 8.0。

我得到了1280x720分辨率的视频流(当手机连接到主板时来自Android Auto),但内容包含在我屏幕大小为900x500的视频中。

基本上,如果您看到完整的视频,在较大的框架内有一张较小的图像,黑边距为(1280-900)/2=190和(720-500)/2=110,如下图所示:

Video frame image example

(我有一张图片要放在这里,但由于这是我的第一篇帖子,我不被允许,我将用ascii绘制它。) (编辑:实现了为附加图像创建链接,但我将保留ASCII以防万一)

                                    1280
****************************************************************************
*FULL VIDEO                           |                                    *
*                                    110                                   *
*                                     |                                    *
*            **************************************************            *
*            *                       900                      *            *
*            *                                                *            *
*            *                                                *            *
*            *                                                *            *
*            *                                                *            *
*            *                                                *            *
*----190-----*                     CONTENT                500 *----190-----* 720
*            *                                                *            *
*            *                                                *            *
*            *                                                *            *
*            *                                                *            *
*            *                                                *            *
*            **************************************************            *
*                                     |                                    *
*                                    110                                   *
*                                     |                                    *
****************************************************************************

我使用MediaCodec对视频流进行解码,使用SurfaceView将视频渲染到屏幕上(还需要使用触摸事件)。

使用此设置,完整的1280x720视频将缩小到900x500屏幕,因此整个内容都可以在屏幕上看到(即,整个1280x720内容将缩小到900x500)。我实际上需要的是从侧面裁剪黑色页边距,只使用里面的900x500图像。

这似乎很容易做到,但实际上我在获得解决方案方面遇到了困难。

注意:为了使解释更简单,并使代码片段更容易理解,我使用了带有上面解释的分辨率的硬编码数字,您的代码实际上设置为具有不同的分辨率。

我尝试过的内容:

  1. 使用SurfaceView方法设置LayoutParams并将其传递给带有页边距的布局:

代码片段

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mSurfaceView.getLayoutParams();
layoutParams.width = 900;
layoutParams.height = 500;
layoutParams.setMargins(190, 110, 190, 110);
mSurfaceView.setLayoutParams(layoutParams);
mSurfaceView.requestLayout();

这当然不管用。Layout参数是SurfaceView本身的参数,因此当调用setMargins时,它们被应用到Surface eView以使其更小:900-190*2=520和500-110*2=280。最后,我得到了一个520x280的表面(甚至没有使用孔屏幕),整个视频缩小到了这个大小。因此,最终视频本身的页边距没有被裁剪。

我在这里想的是将表面视图设置为宽度和高度分别为1280和720(表面视图比屏幕本身更粗糙),然后使用setMargins将表面视图裁剪到屏幕的大小。这基本上就是我所需要的。

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mSurfaceView.getLayoutParams();
layoutParams.width = 1280;
layoutParams.height = 720;
layoutParams.setMargins(190, 110, 190, 110);
mSurfaceView.setLayoutParams(layoutParams);
mSurfaceView.requestLayout();

这也不行,因为由于宽度和高度都大于屏幕尺寸,所以Android没有设置为1280x720,而是它能达到的最大尺寸(屏幕尺寸),所以我最终得到了和以前一样的结果。

  1. 使用SurfaceView方法setScaleX和setScaleY

正如我提到的,视频在渲染到表面时会自动缩小,所以我尝试的是将其放大。宽度从1280缩放到900以适应曲面,所以我将其缩放回1280(即将其宽度缩放到1280/900=1.42222...)。高度相同。

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mSurfaceView.getLayoutParams();
layoutParams.width = 900;
layoutParams.height = 500;
mSurfaceView.setLayoutParams(layoutParams);
mSurfaceView.setScaleX(1.44f);
mSurfaceView.setScaleY(1.42f);
mSurfaceView.requestLayout();

这肯定是我想要的最接近的方法。然而,这段视频搞得一团糟,特别是它的颜色。我认为主要原因是例如1280/900有无限个小数,所以这种类型的缩放看起来不是很好。

此外,先缩小,然后再缩小的方法从一开始就听起来并不是很好,因为信息可能会丢失。如果使用更好的数字进行缩放(例如,从1000缩放到500,然后再缩放到1000),可能会得到更好的结果,但仍然不是一个好方法,因为我希望这种方法可以与不同的分辨率一起使用。

  1. MediaCodec缩放以适应裁剪

在MediaCodec端,我尝试了两种缩放模式:

mCodec.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);

and

mCodec.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);

VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CLOPING的声音听起来很有希望,但这只是意味着视频将被裁剪以适应整个表面,但仍保持其纵横比(因此内容不会失真)。

如果MediaCodec中有另一个选项,如VIDEO_Scaling_MODE_NO_Scaling,似乎会很好,但没有...仅上述两个选项。

注意:在测试时,我制作了一个简单的应用程序来简化测试,在其中我会有一个Surface eView,但我没有使用视频,我只是使用了一个1280x720的图像,并用SurfaceView.draw方法绘制了它。这产生了不同的结果,因为图像没有缩放,所以只绘制适合屏幕的内容(中的前900个像素和高度的前500个像素)。如果我可以对MediaCodec执行同样的操作,这将是向前迈进了一步,因为那样我只需要将图像居中,而视频的边距将被省略。

  1. 使用MediaCodec inputBuffer。

我尝试的另一件事是修改MediaCodec解码的数据缓冲区中的视频。 我们的想法是拿起inputBuffer,将其转换为位图,去掉位图的边距,然后将位图转换回ByteBuffer。

在将inputBuffer转换为位图时,我在实现此方法时遇到了一些问题。我开始读到它,以及媒体编解码器如何使用YUV而不是ARGB,但这将取决于来源和YUV的类型。

在我得到任何结果之前,我最终放弃了这个想法,主要是因为你似乎需要知道你使用的是哪种类型的YUV,如果它改变了,解决方案就不会起作用。此外,这似乎不会有最佳的性能,因为您将为每一帧执行ByteBuffer->Bitmap->ByteBuffer的过程。

无论如何,我尝试了更多的东西,但我不想让它比已经存在的更长了。我尝试的大多数东西都与我提到的前两个东西更相关:使用SurfaceView。

关于如何解决此问题,您有什么想法吗?

注意:我可以进行任何必要的更改,例如将SurfaceView更改为TextureView。此外,我正在使用一个定制的电路板,并有AOSP的完全控制,而不仅仅是应用程序。事实上,该应用程序位于AOSP内部,因此我可以使用任何隐藏的方法,甚至可以修改AOSP。然而,这个问题似乎都不是必要的,而且它可以通过任何常规的SDK方法在应用程序中解决。

谢谢并抱歉提出了这么长的问题!这是我的第一个:p

推荐答案

好的,我已经解决了。首先,让我首先指出,在问题中描述的解决方案2(使用SurfaceView方法setScaleX和setScaleY)中,我设置了错误的比例。在我的宽度示例中:1280/900=1.42222...但随后我将其设置为setScaleY,而不是setScaleX。我没有在问题中编辑这个,因为即使这只是一个例子,我在实际代码中也犯了那个错误。

无论如何,我纠正了比例,但我仍然有视频被搞砸的问题。然而,当我将状态栏拉下时,图像只收缩了一点(可能只是一个像素),视频看起来很好,但只要状态栏一恢复,视频就又搞砸了。我读到过SurfaceView可以做这样的事情,所以最好使用纹理视图Insted。所以基本上在读完这篇文章后:

https://www.hellsoft.se/textureview-transforms-on-android/

我决定更改为TextureView并使用setTransform方法。最后,代码如下所示:

float scaleX = (float)width/(float)(width - widthMargin);
float scaleY = (float)height/(float)(height - heightMargin);

Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, (float)mScreenWidth/2f, (float)mScreenHeight/2f);

mTextureView.setTransform(matrix);

对于上面的示例,这将是:

float scaleX = 1280f/1280f - 380f); //380 = 190*2
float scaleY = 720f/720 - 220f); //220 = 110*2

Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, 900f/2f, 500f/2f);

mTextureView.setTransform(matrix);

基本上与我在SurfaceView中尝试使用setScale的方法相同,但使用的是TextureView。

我在问题中提到,我不确定这种方法,因为视频正在缩小,然后再备份。然而,阅读上面链接中的帖子让我理解了视频实际上是如何转换的,以便呈现出来。

TL;DR:使用TextureView和setTransform()方法调整视频大小。

这篇关于Android:SurfaceView中来自MediaCodec的裁剪视频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆