VideoView черная вспышка до и после воспроизведения - PullRequest
16 голосов
/ 03 декабря 2010

У меня есть VideoView, который я хочу использовать для воспроизведения видеоклипа.Я использую его для воспроизведения, и он работает.

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

Однако я вижу черную вспышку непосредственно перед и после фрагмента ролика.Вспышка сама по себе не большая проблема, но ее чернота.Фон белый, поэтому, если вспышка белая или исчезнет, ​​все будет в порядке.

Ответы [ 14 ]

8 голосов
/ 29 июля 2011

Сегодня у меня возникла та же проблема, и я нашел очень плохой и хакерский обходной путь для этой неприятной проблемы: я понял, что можно установить цвет фона / рисование на 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 пикселей сверху.Это был последний взлом, который мне пришлось применить, чтобы заставить его работать - переместите контейнер видео -10dip в пустоту сверху.

Теперь это выглядит потрясающе на моей Galaxy Tab, я не смею проверять егона телефоне SGS2, хотя ...

2 голосов
/ 31 марта 2013

У меня была та же проблема, и белый вместо черного был в порядке для меня .. Я попробовал все решения выше, я придумал следующее

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 мс, начало исчезать из белого, работает для меня как очарование

2 голосов
/ 08 марта 2012

В итоге мне пришлось сделать что-то очень похожее на @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).

В моих экспериментах на разных телефонах этот метод обеспечивал очень быстрое отображение / скрытие маски видео, в отличие от установки / обнуления фона.

1 голос
/ 20 октября 2017

Вот простой трюк

проверьте условие media player.getCurrentPosition == 0 в подготовленном слушателе просмотра видео, черный экран появится, когда позиция будет равна нулю, поэтому покажите изображение довидео будет загружено

mycode:

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;
                            }
                        }

                    }
                });
            }
        }
    });
1 голос
/ 25 июля 2013

Моя вариация на тему @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);
1 голос
/ 01 февраля 2012

Мой обходной путь для этой дьявольской ошибки - использование пустого представления с выбранным цветом фона поверх 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);    
}
0 голосов
/ 09 февраля 2017

Попробуйте эту строку. Это сработало для меня. если (игрок! = NULL) player.release ();

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

 }
0 голосов
/ 20 января 2014

У меня была такая же проблема, это сработало для меня ..

Когда вы хотите показать видео, сделайте videoView.setZOrderOnTop (false);и когда вы хотите скрыть просмотр видео, просто сделайте videoView.setZOrderOnTop (true);

0 голосов
/ 03 мая 2013

Я знаю, что это старый вопрос, но если вы можете добавить код для слушателей MediaPlayer, ответ будет очень простой.Оказалось, что setBackgroundColor для VideoView изменяет цвет переднего плана (https://groups.google.com/forum/?fromgroups=#!topic/android-developers/B8CEC64qYhQ).
Так что единственное, что вам нужно сделать, это переключаться между setBackgroundColor (Color.WHITE) и setBackgroundColor (Color.TRANSPARENT).

0 голосов
/ 23 декабря 2012

Другое решение, которое может работать для людей, которые ищут этот вопрос:

В моем случае, видео было ресурсом в приложении (то есть я знал все об этом, и он не собиралсяизменить) и его последние 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 не гарантирует воспроизведение точно после запрошенного времени)

Затем исполняемый файл проверяет, пришло ли видео во время его паузы, если это так, он приостанавливает его и делает все остальноемы хотим это сделать.Если это не так, он снова запускает postDelayed с собой, в течение более короткого времени (в моем случае 100 мс, но может быть короче)

Конечно, это далеко от идеальногорешение, но это может помочь кому-то с конкретной проблемой, похожей на ту, которая наткнулась на меня на полдня:)

...