reproductor de vídeo android se bloquea reproduciendo archivos locales
Estoy desarrollando una aplicación que realiza bucles de archivos de vídeo locales indefinidamente en ubicaciones minoristas. Estoy usando la clase MediaPlayer de Android para esto, y el desarrollo de la Minix X8 Plus, que es un concentrador multimedia que se ejecuta en la versión de Kodi de Android 4.4.2. El dispositivo reproducirá los videos en una pantalla HDMI.
La mayoría de las veces, el video se reproduce con éxito, pero estoy teniendo problemas con la congelación de la imagen bastante regularmente, mientras que el audio sigue jugando. La frecuencia de la congelación varía de vídeo a video, pero casi siempre al comienzo de la reproducción de vídeo, y puede estar en el primer paso o en los bucles subsiguientes. El reproductor de medios está configurado como loop con mediaPlayer.setLooping(true)
Tengo los siguientes listeners habilitados:
- Establecer brillo / contraste en la captura de vídeo con Android Camera2
- ¿Cómo puedo capturar una grabación de vídeo en Android?
- Pantalla negra en cromo para video html5 de Android
- Transferir flujo de video en tiempo real al servidor mediante Android
- Reproduce dos videos en un VideoView al mismo tiempo
- MediaPlayer.onErrorListener
- MediaPlayer.onInfoListener
- MediaPlayer.OnVideoSizeChangedListener
- MediaPlayer.OnSeekCompleteListener
- MediaPlayer.OnBufferingUpdateListener
- MediaPlayer.OnPreparedListener
Durante los bloqueos, no estoy recibiendo ninguna devolución de llamada, ningún error, ninguna devolución de llamada de información. Creo que el problema debe ser interno a Android MediaPlayer, porque no estoy recibiendo excepciones o errores, y el problema sólo se produce entre el 5-20% del tiempo, dependiendo del video.
Tengo un trabajo alrededor permitido donde supervise la posición del " mediaPlayer.getCurrentPosition()
" video mediaPlayer.getCurrentPosition()
contra la cantidad que el jugador media ha almacenado en búfer MediaPlayer.OnBufferingUpdateListener.onBufferingUpdate()
, que devuelve un porcentaje del archivo video almacenado actualmente. Encuentro que a veces ocurre una congelación y la posición de reproducción se mantiene actualizando como si todo estuviera bien, pero eventualmente la posición de reproducción pasará la cantidad que el jugador ha almacenado en búfer. Cuando esto sucede llamo a mediaPlayer.reset()
y pasar por todo el proceso de configuración de nuevo llamando a mediaPlayer.setDataSource()
y mediaPlayer.prepareAsync()
. También monitoreo para mediaPlayer.getCurrentPosition()
devolviendo la misma posición repetidamente. En algunos videos esto ocurre al principio (pegado en la posición 0) o al final después de que el video se haya congelado (pegado en la última posición, incapaz de realizar bucle).
Me gustaría encontrar otra solución. Todos los vídeos se comportan de manera diferente, e incluso con el restablecimiento del reproductor multimedia, me quedo con una imagen congelada durante el tiempo suficiente como para ser un problema.
He peinado SO para una solución. El problema es similar a esto: SO puesto aquí, pero no idéntico.
Como se sugiere en ese post, estoy votando mediaPlayer.getVideoWidth() and mediaPlayer.getVideoHeight()
hasta que devuelve un número no 0 antes de iniciar la reproducción.
He mirado en ExoPlayer, pero tenía problemas con él se estrelló al reproducir un archivo de vídeo que MediaPlayer de Android no tiene ningún problema con. También leí aquí que no hay mucha ventaja de usar ExoPlayer para reproducir archivos locales.
También he mirado Grafika como una alternativa a MediaPlayer, pero es sólo de vídeo, sin audio, y no es realmente un reproductor multimedia completo.
También he mirado en VLC para Android, pero no hay mucha documentación sobre su uso como una biblioteca con en su aplicación, y todavía se está trabajando en bastante fuerte.
Si alguien ha tenido problemas similares, o podría sugerir una alternativa de código abierto a MediaPlayer de Android, me encantaría saber de usted.
He aquí algunos de los códigos de la actividad que reproduce los videos. Es mucho, he intentado reducirlo al código del reproductor multimedia.
private void initMediaPlayerOnPreparedListener() { try { mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { obtainVideoDimens(); obtainDisplayDimens(); calcVidToDisplayRatio(); applyTransformTextureView(); applyLayoutParamsTextureView(); if (playbackStuckAtZeroReset) { mediaPlayer.seekTo((33)); // should be one one frame into the video at 30 frames per second } mediaPlayer.start(); launchCheckVidProgThread(); Log.d(J, "initMediaPlayerOnPreparedListener() -> mediaPlayer playing: " + mediaPlayer .isPlaying()); MyApplication.setVideoPlaying(true); } }); } catch (IllegalArgumentException | SecurityException | IllegalStateException e) { e.printStackTrace(); } } private void obtainVideoDimens() { videoWidth = 0; videoHeight = 0; do { videoWidth = mediaPlayer.getVideoWidth(); videoHeight = mediaPlayer.getVideoHeight(); Log.d(J, "video width: " + videoWidth + " x video height: " + videoHeight); } while (videoWidth == 0 && videoHeight == 0); } private void obtainDisplayDimens() { DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics); displayWidth = displayMetrics.widthPixels; int displayHeight = displayMetrics.heightPixels; Log.d(J, "real display metrics width pix: " + displayWidth + " x height pix: " + displayHeight); } private void calcVidToDisplayRatio() { videoToDisplayScaleWidth = displayWidth / videoWidth; Log.d(J, "video to display scale width: " + videoToDisplayScaleWidth); } private Matrix applyMatrixScale(float w, float h) { Matrix matrix = new Matrix(); matrix.setScale(w, h); return matrix; } private void initMediaPlayerOnSeekListener() { mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() { @Override public void onSeekComplete(MediaPlayer mp) { } }); } private void initMediaPlayerOnErrorListener() { if (mediaPlayer != null) { mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.d(J, "MediaPlayer error: " + mp.toString() + " what: " + what + " extra: " + extra); stopAndResetMediaPlayer(); return true; } }); } } private void initMediaPlayerOnInfoListener() { if (mediaPlayer != null) { mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.d(J, "MediaPlayer info: " + mp.toString() + " what: " + what + " extra: " + extra); if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { } // never was called, if (what == MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) { Log.d(J, "media player video track lagging!"); } return false; } }); } } private void initMediaPlayerOnBufferingListener() { mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { if (percent > 0) { setLastBuffPercent(percent); } } }); } private static void stopAndResetMediaPlayer() { if (!isMediaPlayerNull()) { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); MyApplication.setVideoPlaying(false); } resetMediaPlayer(); } } private static void resetMediaPlayer() { if (!isMediaPlayerNull()) { Log.d(J, "resetting media player!!!!!!!!"); mediaPlayer.reset(); declareMediaPlayerAttributes(); mediaPlayer.prepareAsync(); } } private void initMediaPlayerOnVideoSizeChangedListener() { mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() { @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.d(J, "onVideoSizeChanged! width: " + width + " height: " + height); if (videoWidth != width || videoHeight != height) { // if obtainVideoDimens returns either dimen to 0, screen goes black, never recovers if (mp.isPlaying()) { mp.pause(); } videoWidth = width; videoHeight = height; calcVidToDisplayRatio(); applyTransformTextureView(); applyLayoutParamsTextureView(); mp.start(); } } }); } private void initMediaPlayerOnSeekCompleteListener() { mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() { @Override public void onSeekComplete(MediaPlayer mp) { Log.d(J, "onSeekComplete!"); launchCheckVidProgThread(); mp.start(); // see if this changes things } }); } private void initMediaPlayerOnCompletionListener() { mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.d(J, "media player onCompletion"); } }); }
Aquí está el hilo de fondo que restablece el reproductor multimedia:
private static class CheckVideoProgressRunnable implements Runnable { private static final int ZERO = 0; private static final int SECONDS_IN_ERROR_BEFORE_RESET = 5; //private static final int POLLS_PER_SECOND = 12; private static final int POLLS_PER_SECOND = 5; private static final int FIFTY = 50; private static final int ONE_HUNDRED = 100; private static final int ONE_THOUSAND = 1000; private static final int BUFFER_PERCENT_SAFETY_THRESHOLD = 15; private static final long SLEEP_TIME = ONE_THOUSAND / POLLS_PER_SECOND; // roughly 5 x per second private int lastPlaybackPosition; private int videoDuration; private int playbackPositionStuckCount; // tracks the amount of times the playback position remains static private int bufferExceededCount; // tracks the amount of times the playback position has exceeded the buffer position + BUFFER_PERCENT_SAFETY_THRESHOLD @Override public void run() { Log.d(J, "start of run in video monitoring thread!!!!!!"); CrashHandler crashHandler = new CrashHandler(context); crashHandler.initExceptionHandler(); boolean newLoop = false; videoDuration = mediaPlayer.getDuration(); int percentOfVideoDurationThreshold = videoDuration / FIFTY; // 2% of video duration int loopCount = ZERO; int playbackStuckThresh = POLLS_PER_SECOND * SECONDS_IN_ERROR_BEFORE_RESET; // if video is stuck for roughly five seconds, reset mediaPlayer Log.d(J, "sleep time millis: " + SLEEP_TIME); Log.d(J, "playbackStuckThresh: " + playbackStuckThresh); Log.d(J, "percent of video duration threshold: " + percentOfVideoDurationThreshold); Log.d(J, "video duration: " + videoDuration); while (!isMediaPlayerNull()) { loopCount++; try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException e) { e.printStackTrace(); } int currentPos = ZERO; int currentPlaybackPercent = ZERO; int buffPercent = ZERO; if (!isMediaPlayerNull()) { currentPos = mediaPlayer.getCurrentPosition(); if (currentPos == lastPlaybackPosition) { playbackPositionStuckCount++; } else { lastPlaybackPosition = currentPos; playbackPositionStuckCount = ZERO; } currentPlaybackPercent = (int) (((double) currentPos / videoDuration) * ONE_HUNDRED); buffPercent = getLastBuffPercent(); if (buffPercent > ZERO && (buffPercent + BUFFER_PERCENT_SAFETY_THRESHOLD) < currentPlaybackPercent) { bufferExceededCount++; } boolean playbackStuck = playbackPositionStuckCount > playbackStuckThresh; boolean bufferExceeded = bufferExceededCount > playbackStuckThresh; if (((bufferExceeded) || playbackStuck)) { Log.d(J, "buffer: " + buffPercent + "%"); Log.d(J, "position: " + currentPlaybackPercent + "%"); Log.d(J, "playback stuck count: " + playbackPositionStuckCount); Log.d(J, "buffer exceeded count: " + bufferExceededCount); Log.d(J, "buffer exceeded: " + bufferExceeded + " playback stuck: " + playbackStuck); if (currentPos == 0) { //handler.post(new MediaPlayerSeekRunnable(videoDuration)); handler.post(new ResetMediaPlayerRunnable()); playbackStuckAtZeroReset = true; break; } else { handler.post(new ResetMediaPlayerRunnable()); break; } } } // for setting the volume the video loops (volume does not stay muted on loop without this) if (!isMediaPlayerNull()) { if (currentPos < percentOfVideoDurationThreshold && newLoop) { if (volumeMuted) { float zero = Constants.ZERO_FLOAT; mediaPlayer.setVolume(zero, zero); Log.d(J, "mediaPlayer volume: " + zero); } else { float loopingVolume = MyApplication.getCurrentDeviceVolume(); mediaPlayer.setVolume(loopingVolume, loopingVolume); Log.d(J, "mediaPlayer volume: " + loopingVolume); } Log.d(J, "video looping!!!!!!!!!!!!!!!!!!! volume muted: " + volumeMuted); newLoop = false; Log.d(J, "new loop set: " + newLoop); } } if (videoDuration - currentPos < percentOfVideoDurationThreshold && !newLoop) { newLoop = true; Log.d(J, "new loop set: " + newLoop); } // these conditions are for logging only if (newLoop) { Log.d(J, "current position: " + currentPos + " duration: " + videoDuration); } if (loopCount % (ONE_THOUSAND / SLEEP_TIME) == ZERO) { // once a second Log.d(J, "current position: " + currentPos); Log.d(J, "position: " + currentPlaybackPercent + "%"); Log.d(J, "buffer: " + buffPercent + "%"); } } Log.d(J, "video monitoring thread hit break"); } }
- Orientación de vídeo de retrato de Android incorrecta en VideoView
- Cómo ocultar la ventana Intent Chooser en android?
- Android Marshmallow "No se puede reproducir este video" error
- Reproducción de vídeo desde el directorio caché de la aplicación
- ¿Reproduce vídeos de vimeo usando el reproductor nativo de android?
- Cómo reproducir vídeos de YouTube en WebView en Amazon Fire TV?
- Reproducir video local en la vista web
- Android: onSeekCompleteListener con VideoView
- iPhone5S como modelo de dispositivo Android en Playstore Device Listing?
- Android TextView Problema de enfoque en listview (sólo MarshMallow)