Подтвердить что ты не робот

VideoView onResume теряет буферную часть видео

У меня есть Activity, в котором есть

  • VideoView  - Потоковое видео с веб-сервера.

  • Кнопка -  Заставляет пользователя перейти к следующему действию.

Когда приложение запускается,  VideoView создан для воспроизведения видео с веб-сервера.

Теперь предположим

 Total Video length is 60 Minutes

 Current Video progress is 20 Minutes

 Current Buffered progress 30 Minutes 

Теперь, когда я нажимаю на вышеупомянутую кнопку, которая возвращает пользователя к следующему действию.

Из этого действия, если я нажму кнопку "Назад", перед пользователем появится предыдущая активность (с VideoView и Button). Но при возобновлении все Буферизованное Порция видео потеряно, и, следовательно, VideoView начинает воспроизводить видео с самого начала, что очень плохо. < - Актуальная проблема

Проблема

Когда активность возобновляется, буферизованная часть видео теряется и, следовательно, снова начинает буферизацию. Итак, как преодолеть повторную буферизацию буферизованной части видео?

Даже официальное приложение для Android. имеет ту же проблему.

Изменить 1:

Я попробовал приведенный ниже код в Activity, но не работал.

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

Может ли кто-нибудь вести меня по этой проблеме? Или я что-то упустил, чтобы сделать эту работу безупречной?

Текущее обходное решение

Я сохранил текущую позицию воспроизведения видео в методе onPause() и в методе onResume() я использовал эту позицию для поиска видео до этой продолжительности. Это прекрасно работает. Но буферизация видео начинается с начала, когда он запускает видео из позиции поиска.

Любая помощь глубоко оценена.

4b9b3361

Ответ 1

Я потратил несколько часов, пытаясь взломать исходный исходный код VideoView, и теперь я могу подтвердить, что VideoView можно взломать к поведению, что вы хотите - сохранить буферизацию после разрушения поверхности. Я тестировал на своем Samsung Galaxy S2, который работает, как ожидалось, в моем случае, буферизация видео (потоковое видео m4v с удаленного http-сервера) успешно сохраняется при открытии нового действия и возвращении.

В принципе, обходным путем является создание собственного класса VideoView (путем копирования исходного кода) и взломать реализацию SurfaceHolder.Callback(). Имейте в виду, что VideoView использует некоторые внутренние/скрытые API, поэтому, если вы хотите создать копию VideoView в своем собственном проекте, вы должны следовать inazaruk article, чтобы включить использование внутреннего/скрытого API. В качестве быстрого взлома, я просто скачаю inazaruk build из здесь и использую inazaruk-android-sdk-dbd50d4/platform/android-15-internals/android.jar замените мой оригинальный android.jar в моем android-sdk/platform/android-15/.

Исходный код VideoView можно загрузить с GrepCode. Когда вы успешно создадите свою собственную копию без ошибки компиляции, измените SurfaceHolder.Callback() на что-то вроде этого:

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 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 public methods stopPlayback() and suspend(), make
         * sure you don't call them in your activity.
         */
        pause(); // <-- don't release, just pause.
    }
};

И убедитесь, что вы не вызываете videoView.resume(), videoView.setVideoURI(), videoView.suspend() и videoView.stopPlayback() явно в вас MediaPlayerActivity, как это:

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

Обратите внимание, что я только что сделал грязный взломать, чтобы доказать выполнимость, вы должны правильно спроектировать и реализовать свой класс VideoView, чтобы избежать побочного эффекта.

Update:

В качестве альтернативы вы можете добиться такого же эффекта, используя простой MediaPlayer, создайте MediaPlayerActivity, если вы не хотите использовать материал interal/hide API. Вы можете начать с примера MediaPlayerDemo_Video.java в ApiDemos. Ключевым моментом является то, что подготовка (буферизация результатов) и метод выпуска должным образом обрабатываются как методом обратного вызова SurfaceHolder, так и методом жизненного цикла активности, чтобы избежать подготовки/выпуска видео каждый раз, когда поверхность создается/уничтожается, а активность запускается, возобновляется/приостанавливается, остановился. Я создал фиктивный BufferedMediaPlayerActivity (очень упрощен для публикации здесь), который содержит только ключевые части и может быть использован для быстрой демонстрации, у него нет MediaController, однако вы можете проверить из Logcat, чтобы увидеть, что процент буфера фактически сохраняется увеличиваясь, а не перекатываясь с 0 каждый раз, когда вы открываете новую активность и возвращаетесь.

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>

Ответ 2

По мере того, как буфер теряется, когда видеоизображение переходит к фону (изменение видимости), вы должны попытаться заблокировать это поведение, переопределив метод onWindowVisibilityChanged VideoView. Вызовите супер, только если вид видео становится видимым. Может иметь побочные эффекты.

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

Ответ 3

Вы пробовали 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) {
                }
}

Ответ 4

Я нашел решение исправить его:

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

Он работает для меня, я уверен, что он поможет вам

Ответ 5

Проблема с videoView.resume() в onResume() можно увидеть здесь: VideoView.resume(). videoView.resume() вызывает openVideo(), который сначала выпускает все предыдущие экземпляры MediaPlayer, а затем запускает новый. Я не вижу легкого выхода из этого.

Я вижу две возможности:

  • Напишите свой собственный VideoView, который сохранит экземпляр MediaPlayer для сколько хочешь. Или просто взять источник и изменить по своему вкусу, это открытый исходный код (проверьте лицензию, хотя).
  • Создайте сетевой прокси в своем приложении, который стоит между VideoView и веб-сервер. Вы указываете свой прокси-сервер на веб-сервер и VideoView для прокси. Прокси-сервер начинает загрузку данных, сохраняет их непрерывно для последующего использования и передает его на прослушивающий MediaPlayer (который был начатый VideoView). Когда MediaPlayer отключается, вы сохраняете уже загруженные данные, так что, когда MediaPlayer перезапустит воспроизведения, вам не нужно его снова загружать.

Удачи!:)

Ответ 7

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

попробуйте добавить выше двух методов в свою активность.

Ответ 8

Вы упомянули две отдельные проблемы, и пока я не знаю, как сохранить буферизованное видео, вы все же можете не начинать с самого начала, вызывая getCurrentPosition в onPause и seekTo в onResume. Этот вызов является асинхронным, но он может дать вам частичное решение.

Ответ 9

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

Ответ 10

В функции onPause() вместо

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

попробовать

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