studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android video-streaming videoview android-video-player

para - manual de programacion android pdf



VideoView onResume pierde parte del video almacenada en el buffer (10)

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 muestra.

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

Ahora asume

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, el usuario lleva a la siguiente actividad.

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

Problema

Cuando la actividad se reanuda, la parte del video almacenada en el búfer se pierde y, por lo tanto, comienza a almacenarlo nuevamente. Entonces, ¿cómo superar el re-buffering la parte del video almacenada en búfer?

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

Edición 1:

Probé el siguiente código en Actividad 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 perfectamente?

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 con esa duración. Esto funciona bien. Pero el búfer de video comienza desde el principio, pero comienza el video desde la posición de búsqueda.

Cualquier ayuda es muy apreciada.


¿Has intentado buscar ()

@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) { } }


Como el búfer se pierde cuando la vista de video pasa al fondo (cambio en la visibilidad), debe intentar bloquear este comportamiento anulando el método VideoView de VideoView . Llama super solo si la vista de video se está haciendo 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); } } }


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

Veo dos posibilidades:

  • Escriba su propio VideoView que retenga la instancia de MediaPlayer durante el tiempo que desee. O simplemente tome la fuente y modifíquela a su gusto, es una fuente abierta (aunque verifique la licencia).
  • Cree un proxy de red en su aplicación que se interponga entre VideoView y el servidor web. Usted apunta su proxy al servidor web y el VideoView al proxy. El proxy comienza a descargar los datos, los guarda continuamente para su uso posterior y los transfiere al MediaPlayer de escucha (que fue iniciado por VideoView). Cuando el MediaPlayer se desconecta, conserva los datos ya descargados, de modo que cuando el MediaPlayer reinicia la reproducción, no tiene que descargarla nuevamente.

¡Que te diviertas! :)


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


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



He encontrado 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í, seguro que te ayuda.


He pasado varias horas tratando de piratear el código fuente original de VideoView y ahora puedo confirmar que se puede piratear el comportamiento de lo que desee: retener el almacenamiento en búfer después de que se destruya la superficie. He probado 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 correctamente cuando abro una nueva actividad y vuelvo.

Básicamente, la solución es crear su propia clase VideoView (copiando el código fuente) y hackear la implementación SurfaceHolder.Callback (). Tenga en cuenta que VideoView usa algunas API internas / ocultas, 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 las API internas / ocultas. Como truco rápido, solo descargo la compilación de inazaruk desde here y uso inazaruk-android-sdk-dbd50d4 / plataformas / android-15-internals / android.jar para reemplazar mi android.jar original en mi android-sdk / plataformas / android-15 /.

El código fuente de VideoView se puede descargar desde GrepCode . Una vez que haya creado con éxito su propia copia sin error de compilació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 a videoView.resume (), videoView.setVideoURI (), videoView.suspend () y videoView.stopPlayback () explícitamente en su MediaPlayerActivity como esto:

@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 demostrar la viabilidad. Debe diseñar e implementar su clase VideoView correctamente para evitar cualquier efecto secundario.

Actualizar:

Como alternativa, debería poder lograr el mismo efecto utilizando MediaPlayer simple; cree su MediaPlayerActivity si no quiere hacer las cosas de la API interal / hide Puede comenzar con el MediaPlayerDemo_Video.java en el ejemplo de ApiDemos. El punto clave es asegurarse de que la preparación (búfer de resultados) y el método de liberación se manejen correctamente tanto en los métodos de devolución de llamada de SurfaceHolder como en el método del ciclo de vida de la actividad para evitar la preparación / liberación de video cada vez que se crea / destruye la superficie, y la actividad se inicia, reanuda / pausa detenido. He creado un BufferedMediaPlayerActivity ficticio (muy simplificado para la publicación aquí) que contiene solo partes clave y se puede usar para demostraciones rápidas, no tiene MediaController, sin embargo, puede verificar desde Logcat para ver que el porcentaje del búfer se mantiene. aumentar 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:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <SurfaceView android:id="@+id/surface" android:layout_width="200dip" android:layout_height="160dip" android:layout_gravity="center"> </SurfaceView> </LinearLayout>


@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 añadiendo los métodos anteriores en su actividad.


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