流媒体 播放 理论篇
流媒体播放
?
之所以为理论篇 因为该篇仅实现了播放功能 但还有一些其他待解决/完善功能 所以称之为理论篇
?
而且该篇以原理居多 故暂不释放源码
?
?
?
[原理]
public void startStreaming(final String mediaUri){ Runnable r = new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stubtry {readStream(mediaUri);} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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(){@Overridepublic void run() {// TODO Auto-generated method stub//initial MediaPlayer if nullif(mp == null){if(loadByte >= INTIAL_KB_BUFFER){loadByte = 0;startMediaPlayer();}}//load buffer while 1000else 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, FilenewLocation) 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(); }?
?
9. done, plx test it, post feedback, thanks
?
1 楼 bingtian 2010-05-28 你好,这个如果已经播放缓冲的,音乐停止了,等缓冲开始多了,音乐还会继续播放吗? 2 楼 gryphone 2010-05-28 bingtian 写道你好,这个如果已经播放缓冲的,音乐停止了,等缓冲开始多了,音乐还会继续播放吗?是的啊 但是从效果看不是太好 我现在也在寻找更smooth的方法