VideoView черная вспышка до и после воспроизведения

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

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

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


person vincentkriek    schedule 03.12.2010    source источник
comment
Простое решение находится в: stackoverflow.com/questions/ 9765629/   -  person user517708    schedule 16.10.2013


Ответы (14)


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

person Thomas Keller    schedule 29.07.2011

В итоге мне пришлось сделать что-то очень похожее на @tommyd, чтобы избежать черной вспышки SurfaceView в начале и конце моих видео. Однако я обнаружил, что установка/обнуление фона, отображаемого для видеопросмотра, не происходит мгновенно на многих телефонах. Между моим вызовом для установки фона и его фактическим отображением может быть задержка примерно в полсекунды.

В итоге я создал собственный 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 с черным экраном).

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

person greg7gkb    schedule 08.03.2012
comment
Пожалуйста, дайте мне, как использовать SolidSurfaceView в xml - person Ashish Sahu; 20.05.2014

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

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

person Ahmad Dwaik 'Warlock'    schedule 31.03.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);
person MattD    schedule 25.07.2013

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

проверьте условие 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;
                            }
                        }

                    }
                });
            }
        }
    });
person uma    schedule 20.10.2017
comment
Спасибо большое Ума! Только это решение сработало и для меня с минимальными усилиями. Работает волшебно и имеет смысл. Большое спасибо :) - person Mohit Singh; 22.05.2018

Мой обходной путь для этой дьявольской ошибки использовал пустой вид с выбранным цветом фона поверх видеовида.

<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);    
}
person elprl    schedule 01.02.2012

Эта вспышка возникает из-за изменения текущего представления контента на другое. Вы можете попробовать добавить VideoView в XML-файл макета, а затем сослаться на него с помощью (VideoView) findViewById(R.id.vid); вместо new VideoView(this); и настроить представление содержимого для этого макета.

person Subasteve    schedule 18.04.2011
comment
Черное мигание также появляется при поиске просмотра видео через findViewById, поэтому, к сожалению, это не решение. - person Mathias Conradt; 23.06.2011
comment
Я вижу черную вспышку при первом воспроизведении видео. Проблема в том, что весь пользовательский интерфейс страдает черной вспышкой, хотя видеопросмотр использует только 50% пользовательского интерфейса !! - person Someone Somewhere; 14.01.2012

Почему бы не использовать стили и темы

Можете ли вы попробовать это? Это может помочь

цвета.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">
person Manish Singh    schedule 13.07.2011

Где вы меняете contentView (в каком методе вы написали четыре строки кода выше)?

Вы должны установить contentView только в методе onCreate() объекта Activity. Если вы делаете это где-то еще (например, в обратном вызове кнопки), вы должны вместо этого начать новую активность.

person Guillaume Brunerie    schedule 13.07.2011

Есть одна альтернатива: для этого вы используете медиа-контроллер. Вот пример кода

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

Попробуй это.

person Datta Kunde    schedule 15.07.2011

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

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

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

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

person Daniel    schedule 23.12.2012

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

person Xaon    schedule 03.05.2013

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

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

person M. Usman Afzal    schedule 20.01.2014

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

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

 }
person AlphaStack    schedule 09.02.2017