Yes, as you are thinking, this is a issue in VideoView
, similar issues also appear in MediaPlayer
, and I've encountered similar and strange issues as you did, I had problems when the video was played only on 3G and not on Wi-Fi. This usually happens on 2.1 and some 2.2 devices, but not on higher API levels as I've seen so far.
So what I can recommend is do the following :
First check if the running device may be one that can have issues, something like this :
//Define a static list of known devices with issues
static List sIssueDevices=Arrays.asList(new String[]{"HTC Desire","LG-P500","etc"});
if(Build.VERSION.SDK_INT<9){
if(sIssueDevices.contains(Build.Device){
//This device may have issue in streaming, take appropriate actions
}
}
So this was the simplest part, to detect if the running device may have issues in streaming the video. Now, what I did and may also help you, is buffer the video from Youtube in a file on the SDCard
and set that file as the source
for your VideoView
. I will write some code snippets to see how my approach was :
private class GetYoutubeFile extends Thread{
private String mUrl;
private String mFile;
public GetYotubeFile(String url,String file){
mUrl=url;
mFile=file;
}
@Override
public void run() {
super.run();
try {
File bufferingDir=new File(Environment.getExternalStorageDirectory()
+"/YoutubeBuff");
File bufferFile=new File(bufferingDir.getAbsolutePath(), mFile);
//bufferFile.createNewFile();
BufferedOutputStream bufferOS=new BufferedOutputStream(
new FileOutputStream(bufferFile));
URL url=new URL(mUrl);
URLConnection connection=url.openConnection();
connection.setRequestProperty("User-Agent", "Mozilla");
connection.connect();
InputStream is=connection.getInputStream();
BufferedInputStream bis=new BufferedInputStream(is,2048);
byte[] buffer = new byte[16384];
int numRead;
boolean started=false;
while ((numRead = bis.read(buffer)) != -1 && !mActivityStopped) {
//Log.i("Buffering","Read :"+numRead);
bufferOS.write(buffer, 0, numRead);
bufferOS.flush();
mBuffPosition += numRead;
if(mBuffPosition>120000 &&!started){
Log.e("Player","BufferHIT:StartPlay");
setSourceAndStartPlay(bufferFile);
started=true;
}
}
Log.i("Buffering","Read -1?"+numRead+" stop:"+mActivityStopped);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void setSourceAndStartPlay(File bufferFile) {
try {
mPlayer.setVideoPath(bufferFile.getAbsolutePath());
mPlayer.prepare();
mPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Another issue will arise when the VideoView
has stopped playing before the end of file, because not enough was buffered in the file. For this you need to set an onCompletionListener()
and if you are not at the end of the video, you should start again the video playback from the last position :
public void onCompletion(MediaPlayer mp) {
mPlayerPosition=mPlayer.getCurrentPosition();
try {
mPlayer.reset();
mPlayer.setVideoPath(
new File("mnt/sdcard/YoutubeBuff/"+mBufferFile).getAbsolutePath());
mPlayer.seekTo(mPlayerPosition);
mPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
In the end, of course the GetYoutubeFile
thread is started in the onCreate()
method :
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init views,player,etc
new GetYoutubeFile().start();
}
Some modifications and adaptation I think will have to be done for this code, and it may not be the best approach, but it helped me, and I couldn't find any alternative.