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

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

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

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

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

Это действие отображается в полноэкранном режиме, а видео (в основном) масштабируется до общего размера экрана (экран 1024×600, видео 960×640). Я говорю в основном , потому что по какой-то неизвестной причине фоновое изображение макета смещается примерно на 10 пикселей поверх. Это был последний взлом, который я должен был применить, чтобы заставить его работать – переместите видеоконтейнер -10dip в пустоту сверху.

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

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

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

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

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

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

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

Если это поможет, проголосуйте за этот ответ.

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

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

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

colors.xml

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

styles.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"> 

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

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

Существует одна альтернатива, для которой вы используете медиаконтроллер. Вот пример кода

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

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

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

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

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

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

Мое изменение в теме @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); 

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

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

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

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