2016-01-24 166 views
3

我正在构建Android应用程序,其中一个ExoPlayer将视频播放到SurfaceView的表面上,并且我正在研究是否可以动态模糊播放的视频。Android:动态模糊表面与视频

由于SurfaceView的表面部分不出现在位图中,所以涉及首先生成视图的位图以模糊的模糊技术将不起作用。

表面和视图用于旧版Android(例如Surface.FX_SURFACE_BLUR)内置的模糊效果,但似乎已在较新的API中弃用。

任何人都可以分享一些关于表面如何动态模糊的见解吗?谢谢。

+0

使用TextureView https://github.com/google/ExoPlayer/issues/98再看看http://stackoverflow.com/questions/26633424/how-to-apply-video-effects-过滤器像棕褐色复古等纹理视图在 –

+0

我明白了。我不清楚你为什么推荐一个TextureView而不是SurfaceView。这有什么不同吗? – Michael

+1

由于您尝试应用滤镜(模糊效果),我认为您需要使用SurfaceTexture - 我建议您查看http://source.android.com/devices/graphics/architecture.html和Show + Capture Camera应用程序Grafika项目:https://github.com/google/grafika –

回答

6

在StackOverflow上有很多问题需要完成。我会回顾一下我用过的方法,希望对某些人有用。

如果这是一个视频帧的静态模糊,那么在TextureView中播放视频就足够了,使用.getBitmap()函数并使用诸如Renderscript之类的工具模糊生成的Bitmap。但是,.getBitmap()在主UI线程上执行,因此滞后于其正在尝试复制帧的视频。

为了对每一帧执行模糊处理,最好的方法似乎是使用带有自定义渲染器的GLSurfaceView。我用VidEffects中指定的代码this answer指出是一个很好的起点。

具有大半径的模糊可能非常计算密集。这就是为什么我第一次接近使用两个单独的片段着色器(一个模糊水平模糊,一个模糊垂直结果)执行模糊。我实际上最终只使用了一个片段着色器来应用7x7高斯内核。如果您的GLSurfaceView较大,请记住一个非常重要的事项是在GLSurfaceViewSurfaceHolder上拨打setFixedSize()以使其分辨率低于屏幕的分辨率。结果看起来不是很像素,因为它无论如何都是模糊的,但是性能的提升非常显着。

模糊我在大多数设备上设置为播放24fps,setFixedSize()指定其分辨率为100x70。

3

如果任何人想要单次传递片段着色器来完成该圆圈...下面的代码实现了从VidEffects可用的代码中定义的ShaderInterface。我改编自this example on ShaderToy.com

public class BlurEffect2 implements ShaderInterface { 

    private final int mMaskSize; 
    private final int mWidth; 
    private final int mHeight; 

    public BlurEffect2(int maskSize, int width, int height) { 
     mMaskSize = maskSize; 
     mWidth = width; 
     mHeight = height; 
    } 

    @Override 
    public String getShader(GLSurfaceView mGlSurfaceView) { 

     float hStep = 1.0f/mWidth; 
     float vStep = 1.0f/mHeight; 

     return "#extension GL_OES_EGL_image_external : require\n" +   
       "precision mediump float;\n" + 
       //"in" attributes from our vertex shader 
       "varying vec2 vTextureCoord;\n" + 

       //declare uniforms 
       "uniform samplerExternalOES sTexture;\n" + 

       "float normpdf(in float x, in float sigma) {\n" + 
       " return 0.39894 * exp(-0.5 * x * x/(sigma * sigma))/sigma;\n" + 
       "}\n" + 


       "void main() {\n" + 
       " vec3 c = texture2D(sTexture, vTextureCoord).rgb;\n" + 

       //declare stuff 
       " const int mSize = " + mMaskSize + ";\n" + 
       " const int kSize = (mSize - 1)/2;\n" + 
       " float kernel[ mSize];\n" + 
       " vec3 final_colour = vec3(0.0);\n" + 

       //create the 1-D kernel 
       " float sigma = 7.0;\n" + 
       " float Z = 0.0;\n" + 
       " for (int j = 0; j <= kSize; ++j) {\n" + 
       "  kernel[kSize + j] = kernel[kSize - j] = normpdf(float(j), sigma);\n" + 
       " }\n" + 

       //get the normalization factor (as the gaussian has been clamped) 
       " for (int j = 0; j < mSize; ++j) {\n" + 
       "  Z += kernel[j];\n" + 
       " }\n" + 

       //read out the texels 
       " for (int i = -kSize; i <= kSize; ++i) {\n" + 
       "  for (int j = -kSize; j <= kSize; ++j) {\n" + 
       "   final_colour += kernel[kSize + j] * kernel[kSize + i] * texture2D(sTexture, (vTextureCoord.xy + vec2(float(i)*" + hStep + ", float(j)*" + vStep + "))).rgb;\n" + 
       "  }\n" + 
       " }\n" + 

       " gl_FragColor = vec4(final_colour/(Z * Z), 1.0);\n" + 
       "}"; 
    } 

} 

正如迈克尔指出上面,你可以通过使用setFixedSize设置SurfaceView的尺寸提高性能。

@BindView(R.id.video_snap) 
VideoSurfaceView mVideoView; 

@Override 
public void showVideo(String cachedPath) { 
    mImageView.setVisibility(View.GONE); 
    mVideoView.setVisibility(View.VISIBLE); 

    //Get width and height of the video 
    final MediaMetadataRetriever mRetriever = new MediaMetadataRetriever(); 
    mRetriever.setDataSource(cachedPath); 
    int width = Integer.parseInt(mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)); 
    int height = Integer.parseInt(mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)); 

    //divide the width and height by 10 
    width /= 10; 
    height /= 10; 

    //set the size of the surface to play on to 1/10 the width and height 
    mVideoView.getHolder().setFixedSize(width, height); 

    //Set up the media player 
    mMediaPlayer = new MediaPlayer(); 
    mMediaPlayer.setLooping(true); 

    try { 
     mMediaPlayer.setDataSource(cachedPath); 
    } catch (Exception e) { 
     Timber.e(e, e.getMessage()); 
    } 

    //init and start the video player with the mask size set to 17 
    mVideoView.init(mMediaPlayer, new BlurEffect2(17, width, height)); 
    mVideoView.onResume(); 
}