21

我有一个 VideoView,我想用它来播放影片剪辑。我像这样使用它来播放它并且它有效。

VideoView vv = new VideoView(this);
vv.setVideoURI(Uri.parse("android.resource://cortex2.hcbj/raw/intro"));
setContentView(vv);
vv.start();

但是,我在影片剪辑前后看到了黑色闪光。闪光灯本身不是什么大问题,但它的黑度却是。背景是白色的,所以如果闪光灯是白色的,或者它消失了就可以了。

4

14 回答 14

11

今天我遇到了同样的问题,并为这个讨厌的问题找到了一个非常糟糕和hacky的解决方法:我意识到可以设置一个背景颜色/可绘制到VideoView它上面混合视频表面并使其完全隐藏。这仅在底层视频仍在播放时有效,而不是在它停止时(无论是正常结束时stopPlayback()还是被调用时),否则您会再次看到黑色闪烁。背景也不能一开始就设置,否则视频会从一开始就完全隐藏。

所以对我来说唯一合乎逻辑的步骤是在我开始视频之前发布一个延迟事件 - 因为我知道视频长度,我让这个事件在它正常结束前几毫秒发生。我截取了 VLC 中最后一帧的屏幕截图,然后将其混合如下:

private void startVideo()
{
    introVideo.setBackgroundDrawable(null);
    introVideo.postDelayed(new Runnable() {
        public void run()
        {
            if (!introVideo.isPlaying())
                return;

            introVideo.setBackgroundResource(R.drawable.video_still_image);
            // other stuff here, for example a custom transition to
            // another activity
        }
    }, 7500); // the video is roughly 8000ms long
    introVideo.start();
}

然而这还不够,因为当视频真正结束时,我仍然会出现短暂的黑屏闪烁,所以我还必须将静止图像设置为包含视频的容器的背景(在我的情况下,它是活动):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:background="@drawable/video_still_image">

    <VideoView android:id="@+id/introVideo"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:layout_alignParentRight="true"
              android:layout_alignParentLeft="true"
              android:layout_alignParentTop="true"
              android:layout_alignParentBottom="true"
              android:layout_marginTop="-10dip" />

</RelativeLayout>

此活动以全屏呈现,视频(大部分)缩放到总屏幕尺寸(屏幕 1024x600,视频 960x640)。我说的主要是,因为由于某种未知的原因,布局的背景图像在顶部混合了大约 10 像素。这是我必须应用的最后一个 hack 以使其工作 - 将视频容器移动-10dip到顶部的空隙中。

这现在在我的 Galaxy Tab 上看起来很棒,但我不敢在 SGS2 手机上测试它……

于 2011-07-29T00:09:05.860 回答
2

我最终不得不做一些与@tommyd 非常相似的事情,以避免在我的视频开始和结束时出现黑色的surfaceView 闪烁。但是,我发现在许多手机上并没有立即为 videoView 设置/清空背景可绘制对象。在我设置背景的调用和实际显示的时间之间可能会有大约半秒的延迟。

我最终做的是创建一个显示单一纯色的自定义 SurfaceView,然后将其覆盖在 VideoView 之上并使用 SurfaceView.setZOrderMediaOverlay()。

我的自定义 SurfaceView 大量了解: http ://android-er.blogspot.com/2010/05/android-surfaceview.html

public class SolidSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = SolidSurfaceView.class.getSimpleName();

    private SolidSurfaceThread mThread;
    private boolean mSurfaceIsValid;
    private int mColor;

    public SolidSurfaceView(Context context) {
        super(context);
        init();
    }

    public SolidSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SolidSurfaceView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        Log.verbose(TAG, "init");

        getHolder().addCallback(this);
        setZOrderMediaOverlay(true);
    }

    public void setColor(int color) {
        mColor = color;
        invalidate();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.verbose(TAG, "surfaceCreated");

        mSurfaceIsValid = true;

        mThread = new SolidSurfaceThread(getHolder(), this);
        mThread.setRunning(true);
        mThread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.verbose(TAG, "surfaceDestroyed");
        mSurfaceIsValid = false;

        boolean retry = true;
        mThread.setRunning(false);
        while (retry) {
            try {
                mThread.join();
                retry = false;
            }
            catch (InterruptedException e) {
                Log.warning(TAG, "Thread join interrupted");
            }
        }
        mThread = null;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if ( ! mSurfaceIsValid) {
            return;
        }

        canvas.drawColor(mColor);
    }

    private static class SolidSurfaceThread extends Thread {

        private final SurfaceHolder mSurfaceHolder;
        private final SolidSurfaceView mSurfaceView;
        private boolean mIsRunning;

        public SolidSurfaceThread(SurfaceHolder surfaceHolder, SolidSurfaceView surfaceView) {
            mSurfaceHolder = surfaceHolder;
            mSurfaceView = surfaceView;
        }

        public void setRunning(boolean running) {
            mIsRunning = running;
        }

        @Override
        public void run() {
            while (mIsRunning) {
                Canvas c = null;
                try {
                    c = mSurfaceHolder.lockCanvas(null);
                    synchronized (mSurfaceHolder) {
                        mSurfaceView.onDraw(c);
                    }
                }
                finally {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null) {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }
    }
}

在托管视图的父活动中:

    mVideoView = (VideoView)findViewById(R.id.video_view);
    mVideoMask = (SolidSurfaceView)findViewById(R.id.video_mask);
    mVideoMask.setColor(Color.BLUE);

然后,您可以执行mVideoMask.setVisibility(View.GONE)隐藏遮罩或mVideoMask.setVisibility(View.VISIBLE)显示遮罩(并隐藏黑屏 VideoView)之类的操作。

在我对各种手机的实验中,这种方法提供了非常快速的视频掩码显示/隐藏,而不是设置/清空背景。

于 2012-03-08T01:49:45.720 回答
2

我有同样的问题,白色而不是黑色对我来说没问题..我尝试了上面的所有解决方案我想出了以下

vv.setBackgroundColor(Color.WHITE);
vv.postDelayed(new Runnable() {
                @Override
                public void run() {
                    vv.setVideoURI(videoUri);
                }
            }, 100);
vv.postDelayed(new Runnable() {
                @Override
                public void run() {
                    vv.setBackgroundColor(Color.TRANSPARENT);
                }
            }, 300);
            vv.requestFocus();
            vv.start();

并延迟了我的视频 400 毫秒开始从白色作品中消失,这对我来说就像一种魅力

于 2013-03-31T13:52:51.360 回答
1

对于这个恶魔般的错误,我的解决方法是在 VideoView 的顶部使用了一个带有选择背景颜色的空白视图。

<View
        android:id="@+id/blankView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@color/white" />

然后在我的代码中我这样做了:

        video = (VideoView) findViewById(R.id.video);
        // set the video URI, passing the vSourse as a URI
        video.setVideoURI(Uri.parse(vSource));
        video.setZOrderOnTop(false);

        SurfaceHolder sh = video.getHolder();
        sh.setFormat(PixelFormat.TRANSPARENT);

        ctlr = new BCDMediaController(this);
        ctlr.setMediaPlayer(video);
        video.setMediaController(ctlr);
        video.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {              
            @Override
            public void onCompletion(MediaPlayer mp) {
                closeActivity();
            }
        });

        blankView = findViewById(R.id.blankView);

        video.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {    
                blankView.postDelayed(new Runnable() {
                        public void run()
                        {
                            blankView.setVisibility(View.GONE);
                        }
                    }, 500);                    
            }
        });

        video.start();
        video.requestFocus();

这为我摆脱了开始的黑色闪光。然后对于最后的黑色闪光,我这样做了:

private void closeActivity() {  
blankView.setVisibility(View.VISIBLE);        
    blankView.postDelayed(new Runnable() {
        public void run()
        {
            video.setOnCompletionListener(null);
            video.stopPlayback();
            video.suspend();
            VideoPlayerViewController.this.finish();
        }
    }, 1);    
}
于 2012-02-01T00:45:28.990 回答
1

我对@tommyd 主题的变体:

将可绘制对象设置为静态视频帧,然后向消息队列发送垃圾邮件。一段时间后,清除可绘制对象以便渲染视频帧。然后,在完成之前,将静态图像设置回来。

    mMovieView.setBackgroundDrawable(bg);
    mMovieView.start();

    final int kPollTime= 25;
    mMovieView.postDelayed(new Runnable() {

        private final int kStartTransitionInThreshold= 50;
        private final int kStartTransitionOutThreshold= 250;

        private boolean mTransitioned= false;
        @Override
        public void run() {
            if (mMovieView.isPlaying()) {
                if (mMovieView.getCurrentPosition() > kStartTransitionInThreshold && !mTransitioned) {
                    mMovieView.setBackgroundDrawable(null); // clear to video
                    mTransitioned= true;
                }

                if (mMovieView.getDuration() - mMovieView.getCurrentPosition() < kStartTransitionOutThreshold)
                    mMovieView.setBackgroundDrawable(bg);
            }

            mMovieView.postDelayed(this, kPollTime); // come back in a bit and try again
        }
    }, kPollTime);
于 2013-07-25T04:36:28.847 回答
1

这是简单的技巧

检查条件media player.getCurrentPosition == 0在准备好的视频视图监听器中,当位置为零时会出现黑屏,因此显示图像直到视频加载

我的代码:

mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(final MediaPlayer mediaPlayer) {
                mVideoView.start();


                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        while (mediaPlayer.isPlaying()) {
                            Log.d("MainActivity", "position " + mediaPlayer.getCurrentPosition());
                            if (mediaPlayer.getCurrentPosition() == 0) {
                                videoStillImageView.setVisibility(View.VISIBLE);
                            } else {
                                videoStillImageView.setVisibility(View.GONE);


                                break;
                            }
                        }

                    }
                });
            }
        }
    });
于 2017-10-20T14:40:37.493 回答
0

该闪光来自将当前内容视图更改为另一个内容视图。您可以尝试将 VideoView 添加到您的布局 xml 文件中,然后使用(VideoView) findViewById(R.id.vid);而不是引用它new VideoView(this);并将内容视图设置为该布局。

于 2011-04-18T23:20:16.837 回答
0

为什么不使用样式和主题

你能试试这个吗?这可能会有所帮助

颜色.xml

<drawable name="transparent">#00000000</drawable>

样式.xml

<style name="Transparent">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">
@android:style/Animation.Translucent
</item>
<item name="android:windowBackground">@drawable/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorForeground">#fff</item>
</style>

要为应用程序的所有活动设置主题,请打开 AndroidManifest.xml 文件并编辑标签以包含带有样式名称的 android:theme 属性。

<application android:theme="@style/Transparent">
于 2011-07-13T14:21:50.610 回答
0

你在哪里改contentView(上面四行代码是用什么方法写的)?

您应该只onCreate()Activity. 如果您在其他地方执行此操作(例如在按钮的回调中),您应该开始一个新活动。

于 2011-07-13T14:26:22.843 回答
0

有一种替代方法是您为此使用媒体控制器。这是示例代码

videoView = (VideoView) this.findViewById(R.id.videoView);
MediaController mc = new MediaController(this);
videoView.setMediaController(mc);
videoView.setVideoURI(Uri.parse("http://c421470.r70.cf2.rackcdn.com/video_5983079.m4v"));
videoView.start();
videoView.requestFocus(); 

试试这个。

于 2011-07-15T04:46:52.540 回答
0

另一个可能适用于正在搜索此问题的人的解决方案:

在我的例子中,视频是应用程序中的一个资源(也就是说,我知道它的一切并且它不会改变)并且它的最后 500 毫秒是同一帧,所以我最终做了以下事情:

mVideoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.splash));
mVideoView.start();

findViewById(android.R.id.content).postDelayed(new WaitForSplashScreenToFinish(), mVideoView.getDuration() - 1000);

并且引用的类是:

private class WaitForSplashScreenToFinish implements Runnable {
    @Override
    public void run() {
        if (mVideoView.isPlaying() && mVideoView.getCurrentPosition() >= mVideoView.getDuration() - 500) {
            mVideoView.pause();

            // Now do something else, like changing activity
        } else {
            findViewById(android.R.id.content).postDelayed(this, 100);
        }
    }
}

说明:开始视频后,我立即创建了一个 Runnable 并将其postDelayed到根视图 ( android.R.id.content) 到视频的持续时间,减去我愿意暂停视频的长度(以及一个大缓冲区,因为 postDelayed 不是t 保证在请求的时间之后播放)

然后,runnable 检查视频是否到达了暂停时间,如果是,则暂停它并执行我们希望它执行的任何其他操作。如果没有,它会再次运行 postDelayed 并缩短时间(在我的情况下为 100 毫秒,但可能更短)

当然,这不是一个理想的解决方案,但它可能会帮助遇到特定问题的人,类似于让我绊倒半天的问题:)

于 2012-12-23T15:13:57.980 回答
0

我知道这是个老问题,但是如果您可以向 MediaPlayer 听众添加一些代码,那么答案非常简单。事实证明, VideoView 的 setBackgroundColor 改变了前景色(https://groups.google.com/forum/?fromgroups=#!topic/android-developers/B8CEC64qYhQ)。
所以你唯一要做的就是在 setBackgroundColor(Color.WHITE) 和 setBackgroundColor(Color.TRANSPARENT) 之间切换。

于 2013-05-03T10:40:37.333 回答
0

我有同样的问题,这对我有用..

当您想显示视频时,请制作 videoView.setZOrderOnTop(false); 当你想隐藏视频视图时,只需制作 videoView.setZOrderOnTop(true);

于 2014-01-20T13:18:31.113 回答
0

试试这条线。这对我有用。if(player!=null)player.release();

@Override
 protected void onDestroy() {
     super.onDestroy();
         player.pause();
     if(player!=null)player.release();
     player = null;
     videoSurface = null;
     controller = null;

 }
于 2017-02-09T14:50:26.180 回答