Я потратил несколько часов, пытаясь взломать исходный исходный код VideoView, и теперь я могу подтвердить, что VideoView можно взломать, чтобы он вел себя так, как вам нужно - сохраняя буферизацию после разрушения поверхности.Я протестировал на своем Samsung Galaxy S2, который работает, как и ожидалось, в моем случае буферизация видео (потоковое видео m4v с удаленного http-сервера) успешно сохраняется, когда я открываю новое действие и возвращаюсь назад.
По сути, обходной путь - создать собственный класс VideoView (путем копирования исходного кода) и взломать реализацию SurfaceHolder.Callback ().Имейте в виду, что VideoView использует некоторый внутренний / скрытый API, поэтому, если вы хотите создать копию VideoView в своем собственном проекте, вы должны следовать статье inazaruk , чтобы включить использование внутреннего / скрытого API.Для быстрого взлома я просто скачиваю сборку Иназарука с здесь и использую inazaruk-android-sdk-dbd50d4 / platform / android-15-internals / android.jar, заменив мой оригинальный android.jar в моем android-sdk/platforms/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'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.
}
};
И убедитесь, что вы не вызываете 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.правильно, чтобы избежать каких-либо побочных эффектов.
Обновление:
В качестве альтернативы, вы должны иметь возможность добиться того же эффекта, используя обычный 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>