Android:使用视频动态模糊表面

时间:2016-01-24 04:31:50

标签: android surfaceview exoplayer

我正在构建一个Android应用程序,其中ExoPlayer在SurfaceView的表面上播放视频,我正在调查是否可以动态模糊播放视频。

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

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

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

2 个答案:

答案 0 :(得分:7)

StackOverflow上有很多问题,需要做的只是一小部分。我将介绍我使用的方法,希望它对某些人有用。

如果这是视频帧的静态模糊,仅使用$i = 1; // initialize counter $dom = new DOMDocument; @$dom->loadHTML($Content); // load the markup $sections = $dom->getElementsByTagName('section'); // get all section tags foreach($sections as $section) { // for each section tag // get div inside each section foreach($section->getElementsByTagName('h2') as $h2) { if($h2->getAttribute('class') == 'h2Article') { // if this div has class maindiv $h2->setAttribute('id', 'a' . $i); // set id for div tag $h2->setAttribute('data-target', '#b' . $i); } } foreach($section->getElementsByTagName('div') as $div) { if($div->getAttribute('class') == 'divArticle') { // if this div has class divArticle $div->setAttribute('id', 'b' . $i); // set id for div tag } if($div->getAttribute('class') == 'divClose') { // if this div has class maindiv $div->setAttribute('data-target', '#b' . $i); // set id for div tag } } $i++; // increment counter } // back to string again, get all contents inside body $Content = ''; foreach($dom->getElementsByTagName('body')->item(0)->childNodes as $child) { $Content .= $dom->saveHTML($child); // convert to string and append to the container } $Content = str_replace('data-target', 'data-toggle="collapse" data-target', $Content); $Content = str_replace('<div class="divArticle', '<div class="divArticle collapse in article', $Content); 播放视频就足够了,使用TextureView函数并使用Renderscript等工具模糊生成的位图。但是,.getBitmap()在主UI线程上执行,因此滞后于其尝试复制的帧。

要为每个帧执行模糊,最好的方法似乎是将GLSurfaceView与自定义渲染器一起使用。我使用VidEffects中指向的this answer中提供的代码作为一个很好的起点。

具有大半径的模糊可能是非常计算密集的。这就是为什么我首先使用两个单独的片段着色器(一个用于水平模糊,一个用于垂直模糊结果)来处理模糊。实际上我最终只使用一个片段着色器来应用7x7高斯内核。如果.getBitmap()很大,请务必注意一点:GLSurfaceView setFixedSize()上的GLSurfaceView要使其分辨率低于屏幕分辨率。结果看起来并不是非常像素化,因为它无论如何都是模糊的,但性能提升非常显着。

我在大多数设备上模糊了24fps,SurfaceHolder指定其分辨率为100x70。

答案 1 :(得分:4)

如果有人想要单个传递片段着色器来完成圆圈......以下代码实现了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" +
                "}";
    }

}

正如Michael上面所指出的那样,您可以使用SurfaceView设置setFixedSize的大小来提高效果。

@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();
}