VideoView onResume pierde la porción de búfer del video

Estoy teniendo una actividad en la que hay

  1. VideoView: transmite un video desde un servidor web.

  2. Botón: lleva al usuario a la siguiente actividad que se mostrará.

Cuando se inicia la aplicación, VideoView está hecho para reproducir el video desde un servidor web.

Ahora asum

Total Video length is 60 Minutes Current Video progress is 20 Minutes Current Buffered progress 30 Minutes 

Ahora cuando hago clic en el botón mencionado anteriormente, lleva al usuario a la siguiente actividad.

Desde esa Actividad, si presiono el botón Atrás, la Actividad Anterior (con VideoView y Botón) aparece frente al usuario. Pero cuando se reanuda, se pierde toda la porción almacenada en el búfer del video y, por lo tanto, VideoView comienza a reproducir el video desde el principio, lo cual es realmente malo. <- Problema real

Problema

Cuando se reanuda la actividad, la porción de búfer del video se pierde y, por lo tanto, comienza a almacenarla de nuevo. Entonces, ¿cómo superar la restauración de la porción de búfer del video?

Incluso la aplicación de Android oficial de Youtube. tiene el mismo problema

Editar 1:

Probé el siguiente código en Activity pero no funciona.

 @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); videoView.suspend(); } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); videoView.resume(); } 

¿Alguien puede guiarme con respecto a este problema? ¿O me estoy perdiendo algo para que esto funcione a la perfección?

Solución actual

He guardado la posición de reproducción actual del video en el método onPause() y en el método onResume() he usado esa posición para buscar el video a esa duración. Esto funciona bien Pero el almacenamiento en búfer de video comienza desde el principio aunque comienza el video desde la posición de búsqueda.

Cualquier ayuda es muy apreciada.

He pasado varias horas tratando de hackear el código fuente original de VideoView y ahora puedo confirmar que VideoView puede ser pirateado según el comportamiento que desee: conserve el almacenamiento en búfer después de que se destruya la superficie. Probé en mi Samsung Galaxy S2, que funciona como se esperaba, en mi caso, el almacenamiento en búfer de video (transmisión de video m4v desde un servidor http remoto) se retiene exitosamente cuando abro una nueva actividad y regreso.

Básicamente, la solución consiste en crear su propia clase VideoView (copiando el código fuente) y piratear la implementación de SurfaceHolder.Callback (). Tenga en cuenta que VideoView utiliza alguna API interna / oculta, por lo que si desea crear una copia de VideoView en su propio proyecto, debe seguir el artículo de inazaruk para habilitar el uso de la API interna / ocultar. Como un truco rápido, acabo de descargar la comstackción de inazaruk desde aquí y uso inazaruk-android-sdk-dbd50d4 / platforms / android-15-internals / android.jar reemplazar mi android.jar original en mi android-sdk / platforms / android-15 /.

El código fuente de VideoView se puede descargar desde GrepCode . Una vez que haya creado correctamente su propia copia sin error de comstackción, cambie SurfaceHolder.Callback () a algo como esto:

 private boolean videoOpened = false; SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { ... ... public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "---------------------> surface created."); mSurfaceHolder = holder; if (!videoOpened) { openVideo(); // <-- if first time opened, do something as usual, video is buffered. /** * openVideo() actually mMediaPlayer.prepareAsync() is the first key point, it is * also called in other two VideoView's public methods setVideoURI() and resume(), * make sure you don't call them in your activity. */ videoOpened = true; } else { start(); // <-- if back from another activity, simply start it again. } } public void surfaceDestroyed(SurfaceHolder holder) { Log.i(TAG, "---------------------> surface destroyed."); // after we return from this we can't use the surface any more. mSurfaceHolder = null; if (mMediaController != null) mMediaController.hide(); //release(true); /** * release() actually mMediaPlayer.release() is the second key point, it is also * called in other two VideoView's public methods stopPlayback() and suspend(), make * sure you don't call them in your activity. */ pause(); // <-- don't release, just pause. } }; 

Y asegúrese de no llamar explícitamente a videoView.resume (), videoView.setVideoURI (), videoView.suspend () y videoView.stopPlayback () en MediaPlayerActivity de esta manera:

 @Override protected void onResume() { if (videoView != null) videoView.resume(); // <-- this will cause re-buffer. super.onResume(); } @Override protected void onPause() { if (videoView != null) videoView.suspend(); // <-- this will cause clear buffer. super.onPause(); } 

Tenga en cuenta que acabo de hacer un truco sucio para probar la viabilidad. Debe diseñar e implementar su clase VideoView correctamente para evitar cualquier efecto secundario.

Actualizar:

Como alternativa, debería poder conseguir el mismo efecto utilizando Simple MediaPlayer creando su MediaPlayerActivity si no quiere hacer las cosas de interal / hide API. Puede comenzar con el MediaPlayerDemo_Video.java en el ejemplo de ApiDemos. El punto clave es asegurarse de que el método de preparación (búfer de resultados) y liberación se maneje correctamente tanto en los métodos de callback de SurfaceHolder como en el ciclo de vida de la actividad para evitar / preparar video cada vez que se crea / destruye la actividad y se inicia, reanuda / pausa detenido. Creé una actividad ficticia BufferedMediaPlayerActivity (muy simplificada para publicar aquí) que contiene solo partes clave y se puede usar para una demostración rápida, no tiene MediaController, sin embargo, puede verificar desde Logcat para ver que el porcentaje de búfer es en realidad mantener aumentando en lugar de pasar de 0 cada vez que abra una nueva actividad y regrese.

BufferedMediaPlayerActivity.java:

 package com.example; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnBufferingUpdateListener; import android.media.MediaPlayer.OnPreparedListener; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; public class BufferedMediaPlayerActivity extends Activity implements OnPreparedListener, OnBufferingUpdateListener, SurfaceHolder.Callback { private static final String TAG = "BufferedMediaPlayerActivity"; private int mVideoWidth; private int mVideoHeight; private MediaPlayer mMediaPlayer; private SurfaceView mPreview; private SurfaceHolder holder; private String path; private boolean mIsVideoReadyToBePlayed = false; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.buffered_media_player); mPreview = (SurfaceView) findViewById(R.id.surface); holder = mPreview.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.setFixedSize(mVideoWidth, mVideoHeight); // retrieve httpUrl passed from previous activity. path = getIntent().getExtras().getString("videoUrl"); } @Override public void onDestroy() { super.onDestroy(); if (mMediaPlayer != null) { mMediaPlayer.release(); mMediaPlayer = null; } mIsVideoReadyToBePlayed = false; } private void playVideo() { mIsVideoReadyToBePlayed = false; try { // Create a new media player and set the listeners mMediaPlayer = new MediaPlayer(); mMediaPlayer.setDataSource(path); mMediaPlayer.setDisplay(holder); mMediaPlayer.prepare(); mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnBufferingUpdateListener(this); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } catch (Exception e) { Log.e(TAG, "error: " + e.getMessage(), e); } } @Override public void onPrepared(MediaPlayer mediaplayer) { Log.d(TAG, "onPrepared called"); mIsVideoReadyToBePlayed = true; if (mIsVideoReadyToBePlayed) { mMediaPlayer.start(); } } @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { Log.i(TAG, "---------------> " + percent); } @Override public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) { Log.d(TAG, "surfaceChanged called"); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "surfaceCreated called"); if (!mIsVideoReadyToBePlayed) playVideo(); else mMediaPlayer.start(); } @Override public void surfaceDestroyed(SurfaceHolder surfaceholder) { Log.d(TAG, "surfaceDestroyed called"); mMediaPlayer.pause(); } } 

buffered_media_player.xml:

      

Encontré una solución para solucionarlo:

 VideoView videoView; MediaPlayer mp; videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { this.mp = mp; } }); public void pause(){ //NOT videoview.pause(); if (mp != null){ mp.pause(); } } public void resume(){ //NOT videoview.resume(); if (mp != null){ mp.start(); } } 

Funciona para mí, estoy seguro de que te ayuda

Como el búfer se pierde cuando la vista del video pasa al segundo plano (cambio en la visibilidad), debe intentar bloquear este comportamiento anulando el método VideoView de VideoView . Llame super solo si la vista de video se está volviendo visible. Puede tener efectos secundarios.

 public class VideoTest extends VideoView { public VideoTest(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onWindowVisibilityChanged(int visibility) { if (visibility == View.VISIBLE) { super.onWindowVisibilityChanged(visibility); } } } 

¿Has probado seekto ()

 @Override protected void onResume() { super.onResume(); try{ if (video_view != null) { video_view.seekTo(position); video_view.start(); } }catch (Exception e) { } } @Override protected void onPause() { super.onPause(); try{ if (video_view != null) { position = video_view.getCurrentPosition(); video_view.pause(); } }catch (Exception e) { } } 

El problema con videoView.resume() en onResume() se puede ver aquí: VideoView.resume () . VideoView.resume() llama a openVideo() que primero libera cualquier instancia anterior de MediaPlayer y luego inicia una nueva. No puedo ver una salida fácil de esto.

Veo dos posibilidades:

  • Escriba su propio VideoView que conserva la instancia de MediaPlayer durante el tiempo que desee. O simplemente tome la fuente y modifíquela a su gusto, es de código abierto (consulte la licencia).
  • Cree un proxy de red en su aplicación que se encuentre entre VideoView y el servidor web. Dirige su proxy al servidor web y VideoView al proxy. El proxy comienza a descargar los datos, los guarda continuamente para su uso posterior y los pasa al MediaPlayer de escucha (que fue iniciado por VideoView). Cuando el MediaPlayer se desconecta, usted conserva los datos ya descargados, de modo que cuando MediaPlayer reinicie la reproducción, no tenga que volver a descargarlos.

¡Que te diviertas! 🙂

He resuelto una versión que no requiere un VideoView personalizado ni un cambio de configuración manual. Consulte el cambio de orientación de Android VideoView con video almacenado para obtener una explicación.

 @Override protected void onPause() { // TODO Auto-generated method stub videoView.pause(); super.onPause(); } @Override protected void onRestart() { // TODO Auto-generated method stub videoView.resume(); super.onPause(); } 

intente agregando más de dos veces en su actividad.

Ha mencionado dos problemas por separado, y aunque no sé cómo mantener el video almacenado en búfer, aún puede evitar comenzar desde el principio llamando a getCurrentPosition en onPause y seekTo en onResume. Esta llamada es asincrónica, pero podría brindarle una solución parcial.

 public class Video_play extends Activity { VideoView vv; String URL; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.play_video); URL= getIntent().getStringExtra("URL"); vv=(VideoView)findViewById(R.id.videoView1); MediaController mediaController = new MediaController(this); mediaController.setAnchorView(vv); Log.v("URL",URL); // Uri uri = Uri.parse(URL); // vv.setVideoURI(uri); vv.setMediaController(new MediaController(this)); vv.setVideoPath(URL); // vv.requestFocus(); // // vv.start(); // Uri uri=Uri.parse(URL); // // // vv.setVideoURI(uri); vv.start(); } 

En la función OnPause (), en lugar de

 @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); videoView.suspend(); } 

tratar

 @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); videoView.pause(); }