流媒体播放
之所以为理论篇 因为该篇仅实现了播放功能 但还有一些其他待解决/完善功能 所以称之为理论篇
而且该篇以原理居多 故暂不释放源码
[原理]
写道
其实 原理很简单
1. 下载目标URI
2. 当下载了 96*10/8 Byte 开始播放之
3. 一边播放 一边下载
4. 当下载了 100 byte 暂停播放 重置播放目标 并继续播放
5. 下载完成后 重置播放目标 并继续播放
1. 下载目标URI
2. 当下载了 96*10/8 Byte 开始播放之
3. 一边播放 一边下载
4. 当下载了 100 byte 暂停播放 重置播放目标 并继续播放
5. 下载完成后 重置播放目标 并继续播放
[代码 步骤]
1. 开辟Thread
public void startStreaming(final String mediaUri){
Runnable r = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
try {
readStream(mediaUri);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
new Thread(r).start();
}
2. 创建目标URI 并下载之
public void readStream(String mediaUri) throws MalformedURLException, IOException{
URLConnection uc = new URL(mediaUri).openConnection();
uc.connect();
InputStream is = uc.getInputStream();
if(is == null){
//error, InputStream is null
}
dlMedia = new File(context.getCacheDir(),"downloadingMedia.dat");
if(dlMedia.exists()){
dlMedia.delete();
}
FileOutputStream fo = new FileOutputStream(dlMedia);
byte buf[] = new byte[16384];
loadByte = 0;
do {
int numread = is.read(buf);
loadByte += numread;
if(numread <= 0){//end of stream, so exist
break;
}
fo.write(buf, 0, numread);
testMediaBuffer();
}
while(true);
is.close();
//buffer all stream to MediaPlayer if end of stream
transferBufferToMediaPlayer();
3. 根据此刻下载的byte 判断是:开始播放 还是 缓冲下载数据
public void testMediaBuffer(){
Runnable update = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
//initial MediaPlayer if null
if(mp == null){
if(loadByte >= INTIAL_KB_BUFFER){
loadByte = 0;
startMediaPlayer();
}
}//load buffer while 1000
else if(loadByte > BUFFER_KEY_BYTE) {
loadByte = 0;
transferBufferToMediaPlayer();
}
}
};
handler.post(update);
}
4. 开始播放 并跟踪播放进度
public void startPlayProgressUpdater() {
//float progress = mp.getCurrentPosition();
if (mp.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
startPlayProgressUpdater();
}
};
handler.postDelayed(notification,100);
}
}
public void startMediaPlayer() {
try {
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
// We double buffer the data to avoid potential read/write errors that could happen if the
// download thread attempted to write at the same time the MediaPlayer was trying to read.
// For example, we can't guarantee that the MediaPlayer won't open a file for playing and leave it locked while
// the media is playing. This would permanently deadlock the file download. To avoid such a deadloack,
// we move the currently loaded data to a temporary buffer file that we start playing while the remaining
// data downloads.
moveFile(dlMedia,bufferedFile);
mp = createMediaPlayer(bufferedFile);
// We have pre-loaded enough content and started the MediaPlayer so update the buttons & progress meters.
mp.start();
startPlayProgressUpdater();
} catch (IOException e) {
//Log.e(getClass().getName(), "Error initializing the MediaPlayer.", e);
return;
}
}
5. 缓冲下载数据
private void transferBufferToMediaPlayer() {
try {
//remember current position
int curPosition = mp.getCurrentPosition();
// Copy the currently downloaded content to a new buffered File. Store the old File for deleting later.
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
moveFile(dlMedia,bufferedFile);
// Pause the current player now as we are about to create and start a new one. So far (Android v1.5),
// this always happens so quickly that the user never realized we've stopped the player and started a new one
mp.pause();
// Create a new MediaPlayer rather than try to re-prepare the prior one.
mp = createMediaPlayer(bufferedFile);
mp.seekTo(curPosition);
mp.start();
// Lastly delete the previously playing buffered File as it's no longer needed.
bufferedFile.delete();
}catch (Exception e) {
//error, to print
}
}
6. 文件移动
private void moveFile(File oldLocation, File newLocation)
throws IOException {
if ( oldLocation.exists( )) {
BufferedInputStream reader = new BufferedInputStream( new FileInputStream(oldLocation) );
BufferedOutputStream writer = new BufferedOutputStream( new FileOutputStream(newLocation, false));
try {
byte[] buff = new byte[8192];
int numChars;
while ( (numChars = reader.read( buff, 0, buff.length ) ) != -1) {
writer.write( buff, 0, numChars );
}
} catch( IOException ex ) {
throw new IOException("IOException when transferring " + oldLocation.getPath() + " to " + newLocation.getPath());
} finally {
try {
if ( reader != null ){
writer.close();
reader.close();
}
} catch( IOException ex ){
//Log.e(getClass().getName(),"Error closing files when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
}
}
} else {
throw new IOException("Old location does not exist when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
}
}
7. 播放指定目标
private MediaPlayer createMediaPlayer(File mediaFile)
throws IOException {
MediaPlayer mPlayer = new MediaPlayer();
// It appears that for security/permission reasons, it is better to pass a FileDescriptor rather than a direct path to the File.
// Also I have seen errors such as "PVMFErrNotSupported" and "Prepare failed.: status=0x1" if a file path String is passed to
// setDataSource(). So unless otherwise noted, we use a FileDescriptor here.
FileInputStream fis = new FileInputStream(mediaFile);
mPlayer.setDataSource(fis.getFD());
mPlayer.prepare();
return mPlayer;
}
8. Toast 信息提示:
public void popMsg(String msg){
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}