Kar*_*iya 30 android video-streaming android-video-player android-videoview
我正在进行活动
VideoView - 从网络服务器流式传输视频.
按钮 - 使用户进入下一个要显示的活动.
应用程序启动时,VideoView将从Web服务器播放视频.
现在假设
Total Video length is 60 Minutes
Current Video progress is 20 Minutes
Current Buffered progress 30 Minutes
Run Code Online (Sandbox Code Playgroud)
现在,当我点击上面提到的Button时,用户将进入下一个活动.
如果按下后退按钮,则从该活动开始,用户前面会显示"上一个活动"(带有VideoView和"按钮").但是当恢复时,视频的所有缓冲部分都会丢失,因此VideoView从一开始就播放视频,这真的很糟糕. < - 实际问题
问题
当Activity重新恢复时,视频的缓冲部分会丢失,因此会再次开始缓冲.那么如何克服重新缓冲视频的缓冲部分呢?
甚至官方Youtube 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();
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以指导我这个问题吗?或者我错过了一些能使这项工作完美的东西?
目前的解决方法
我已经用onPause()方法和onResume()方法保存了视频的当前播放位置,我已经使用该位置来寻找该持续时间的视频.这很好用.但是视频缓冲从一开始就从搜索位置开始视频开始.
非常感谢任何帮助.
yor*_*rkw 24
我花了几个小时试图破解原始的VideoView源代码,现在我可以确认VideoView可以被黑客攻击你想要的行为 - 在表面被破坏后保留缓冲.我已经在我的三星Galaxy S2上进行了测试,按照预期工作,在我的情况下,当我打开一个新活动并返回时,视频缓冲(来自远程http服务器的流式m4v视频)成功保留.
基本上,解决方法是创建自己的VideoView类(通过复制源代码),并破解SurfaceHolder.Callback()实现.请记住,VideoView使用一些内部/隐藏API,因此如果您想在自己的项目中创建VideoView的副本,则必须遵循inazaruk的文章以启用内部/隐藏API.作为一个快速黑客,我只是从这里下载inazaruk的构建并使用inazaruk-android-sdk-dbd50d4/platforms/android-15-internals/android.jar替换我的android-sdk/platforms/android-15中的原始android.jar /.
可以从GrepCode下载VideoView源代码.成功创建自己的副本而没有编译错误后,将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.
}
};
Run Code Online (Sandbox Code Playgroud)
并确保您不要在MediaPlayerActivity中明确调用videoView.resume(),videoView.setVideoURI(),videoView.suspend()和videoView.stopPlayback(),如下所示:
@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();
}
Run Code Online (Sandbox Code Playgroud)
请注意,我刚刚做了一个脏的黑客来证明可行性,你应该正确设计和实现你的VideoView类,以避免任何副作用.
更新:
作为替代方案,如果您不想执行内部/隐藏API内容,则应该能够使用普通MediaPlayer创建MediaPlayerActivity来实现相同效果您可以从ApiDemos示例中的MediaPlayerDemo_Video.java开始.关键是确保在SurfaceHolder Callback方法和Activity生命周期方法中正确处理prepare(结果缓冲)和release方法,以避免每次创建/销毁表面时准备/释放视频,并且Activity已启动,恢复/暂停,停止.我已经创建了一个虚拟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();
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
我找到了解决方法:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
它对我有用,我确定它对你有帮助
小智 -2
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();
}
Run Code Online (Sandbox Code Playgroud)