Android播放远程非流MP4文件

来源:互联网 发布:hadoop windows版本 编辑:程序博客网 时间:2024/04/20 10:05

保存备用:



由于Android自带的Mediaplayer类,只能播放本地或者远程流形式的MP4文件,所以在播放远程非流的MP4,而且MP4的moov数据在文件的末尾时,在下载时,需要我们在写文件时做特殊处理,这样才能实现下载一部分,播放一部视频,下面来看实现代码:

下载代码部分
[java] view plain copy
  1. public class Mp4DownloadUtils {  
  2.     /** 播放MP4消息 */  
  3.     private static final int PLAYER_MP4_MSG = 0x1001;  
  4.     /** 下载MP4完成 */  
  5.     private static final int DOWNLOAD_MP4_COMPLETION = 0x1002;  
  6.     /** 下载MP4失败 */  
  7.     private static final int DOWNLOAD_MP4_FAIL = 0x1003;  
  8.   
  9.     /** 
  10.      * 下载MP4文件 
  11.      * @param url 
  12.      * @param fileName 
  13.      * @param handler 
  14.      * @return 
  15.      */  
  16.     public static File downloadMp4File(final String url, final String fileName,  
  17.             final Handler handler) {  
  18.         final File mp4File = new File(fileName);  
  19.         downloadVideoToFile(url, mp4File, handler);  
  20.         return mp4File;  
  21.     }  
  22.       
  23.     /** 
  24.      * 下载视频数据到文件 
  25.      * @param url 
  26.      * @param dstFile 
  27.      * @param moovSize 
  28.      */  
  29.     private static void downloadVideoToFile(final String url, final File dstFile, final Handler handler) {  
  30.         Thread thread = new Thread() {  
  31.               
  32.             @Override  
  33.             public void run() {  
  34.                 super.run();  
  35.                 try {  
  36.                     URL request = new URL(url);  
  37.                     HttpURLConnection httpConn = (HttpURLConnection) request.openConnection();  
  38.                     httpConn.setConnectTimeout(3000);  
  39.                     httpConn.setDoInput(true);  
  40.                     httpConn.setDoOutput(true);  
  41.                     httpConn.setDefaultUseCaches(false);  
  42.                       
  43.                     httpConn.setRequestMethod("GET");  
  44.                     httpConn.setRequestProperty("Charset""UTF-8");  
  45.                     httpConn.setRequestProperty("Accept-Encoding""identity");  
  46.                       
  47.                     int responseCode = httpConn.getResponseCode();  
  48.                     if ((responseCode == HttpURLConnection.HTTP_OK)) {  
  49.                         // 获取文件总长度  
  50.                         int totalLength = httpConn.getContentLength();  
  51.                         InputStream is = httpConn.getInputStream();  
  52.                           
  53.                         if (dstFile.exists()) {  
  54.                             dstFile.delete();  
  55.                         }  
  56.                         dstFile.createNewFile();  
  57.                         RandomAccessFile raf = new RandomAccessFile(dstFile, "rw");  
  58.                         BufferedInputStream bis = new BufferedInputStream(is);  
  59.                         int readSize = 0;  
  60.                           
  61.                         int mdatSize = 0;// mp4的mdat长度  
  62.                         int headSize = 0;// mp4头长度  
  63.                         byte[] boxSizeBuf = new byte[4];  
  64.                         byte[] boxTypeBuf = new byte[4];  
  65.                         // 由MP4的文件格式读取  
  66.                         int boxSize = readBoxSize(bis, boxSizeBuf);  
  67.                         String boxType = readBoxType(bis, boxTypeBuf);  
  68.                         raf.write(boxSizeBuf);  
  69.                         raf.write(boxTypeBuf);  
  70.                           
  71.                         while (!boxType.equalsIgnoreCase("moov")) {  
  72.                             int count = boxSize - 8;  
  73.                             if (boxType.equalsIgnoreCase("ftyp")) {  
  74.                                 headSize += boxSize;  
  75.                                 byte[] ftyps = new byte[count];  
  76.                                 bis.read(ftyps, 0, count);  
  77.                                 raf.write(ftyps, 0, count);  
  78.                             } else if (boxType.equalsIgnoreCase("mdat")) {  
  79.                                 // 标记mdat数据流位置,在后面reset时读取  
  80.                                 bis.mark(totalLength - headSize);  
  81.                                 // 跳过mdat数据  
  82.                                 skip(bis, count);  
  83.                                 mdatSize = count;  
  84.                                 byte[] mdatBuf = new byte[mdatSize];  
  85.                                 raf.write(mdatBuf);  
  86.                             } else if (boxType.equalsIgnoreCase("free")) {  
  87.                                 headSize += boxSize;  
  88.                             }  
  89.                               
  90.                             boxSize = readBoxSize(bis, boxSizeBuf);  
  91.                             boxType = readBoxType(bis, boxTypeBuf);  
  92.                             raf.write(boxSizeBuf);  
  93.                             raf.write(boxTypeBuf);  
  94.                         }  
  95.                           
  96.                         // 读取moov数据  
  97.                         byte[] buffer = new byte[4096];  
  98.                         int moovSize = 0;  
  99.                         while ((readSize = bis.read(buffer)) != -1) {  
  100.                             moovSize += readSize;  
  101.                             raf.write(buffer, 0, readSize);  
  102.                         }  
  103.                           
  104.                         // 返回到mdat数据开始  
  105.                         bis.reset();  
  106.                         // 设置文件指针偏移到mdat位置  
  107.                         long offset = raf.getFilePointer() - moovSize - mdatSize - 8;  
  108.                         raf.seek(offset);  
  109.                           
  110.                         // 读取mdat数据,设置mp4初始mdat的缓存大小  
  111.                         int buf_size = 56 * 1024;// 56kb  
  112.                         int downloadCount = 0;  
  113.                         boolean viable = false;  
  114.                         while (mdatSize > 0) {  
  115.                             readSize = bis.read(buffer);  
  116.                             raf.write(buffer, 0, readSize);  
  117.                             mdatSize -= readSize;  
  118.                             downloadCount += readSize;  
  119.                             if (handler != null && !viable && downloadCount >= buf_size) {  
  120.                                 viable = true;  
  121.                                 // 发送开始播放视频消息  
  122.                                 sendMessage(handler, PLAYER_MP4_MSG, null);  
  123.                             }  
  124.                         }  
  125.                         // 发送下载消息  
  126.                         if (handler != null) {  
  127.                             sendMessage(handler, DOWNLOAD_MP4_COMPLETION, null);  
  128.                         }  
  129.                           
  130.                         bis.close();  
  131.                         is.close();  
  132.                         raf.close();  
  133.                         httpConn.disconnect();  
  134.                     }  
  135.                 } catch (Exception e) {  
  136.                     e.printStackTrace();  
  137.                     sendMessage(handler, DOWNLOAD_MP4_FAIL, null);  
  138.                 }  
  139.             }  
  140.               
  141.         };  
  142.         thread.start();  
  143.         thread = null;  
  144.     }  
  145.       
  146.     /** 
  147.      * 发送下载消息 
  148.      * @param handler 
  149.      * @param what 
  150.      * @param obj 
  151.      */  
  152.     private static void sendMessage(Handler handler, int what, Object obj) {  
  153.         if (handler != null) {  
  154.             Message msg = new Message();  
  155.             msg.what = what;  
  156.             msg.obj = obj;  
  157.             handler.sendMessage(msg);  
  158.         }  
  159.     }  
  160.       
  161.     /** 
  162.      * 跳转 
  163.      * @param is 
  164.      * @param count 跳转长度 
  165.      * @throws IOException 
  166.      */  
  167.     private static void skip(BufferedInputStream is, long count) throws IOException {  
  168.         while (count > 0) {  
  169.             long amt = is.skip(count);  
  170.             if (amt == -1) {  
  171.                 throw new RuntimeException("inputStream skip exception");  
  172.             }  
  173.             count -= amt;  
  174.         }  
  175.     }  
  176.       
  177.     /** 
  178.      * 读取mp4文件box大小 
  179.      * @param is 
  180.      * @param buffer 
  181.      * @return 
  182.      */  
  183.     private  static int readBoxSize(InputStream is, byte[] buffer) {  
  184.         int sz = fill(is, buffer);  
  185.         if (sz == -1) {  
  186.             return 0;  
  187.         }  
  188.           
  189.         return bytesToInt(buffer, 04);  
  190.     }  
  191.       
  192.     /** 
  193.      * 读取MP4文件box类型 
  194.      * @param is 
  195.      * @param buffer 
  196.      * @return 
  197.      */  
  198.     private static String readBoxType(InputStream is, byte[] buffer) {  
  199.         fill(is, buffer);  
  200.           
  201.         return byteToString(buffer);  
  202.     }  
  203.       
  204.     /** 
  205.      * byte转换int 
  206.      * @param buffer 
  207.      * @param pos 
  208.      * @param bytes 
  209.      * @return 
  210.      */  
  211.     private static int bytesToInt(byte[] buffer, int pos, int bytes) {  
  212.         /* 
  213.          * int intvalue = (buffer[pos + 0] & 0xFF) << 24 | (buffer[pos + 1] & 
  214.          * 0xFF) << 16 | (buffer[pos + 2] & 0xFF) << 8 | buffer[pos + 3] & 0xFF; 
  215.          */  
  216.         int retval = 0;  
  217.         for (int i = 0; i < bytes; ++i) {  
  218.             retval |= (buffer[pos + i] & 0xFF) << (8 * (bytes - i - 1));  
  219.         }  
  220.         return retval;  
  221.     }  
  222.       
  223.     /** 
  224.      * byte数据转换String 
  225.      * @param buffer 
  226.      * @return 
  227.      */  
  228.     private static String byteToString(byte[] buffer) {  
  229.         assert buffer.length == 4;  
  230.         String retval = new String();  
  231.         try {  
  232.             retval = new String(buffer, 0, buffer.length, "ascii");  
  233.         } catch (UnsupportedEncodingException e) {  
  234.             e.printStackTrace();  
  235.         }  
  236.   
  237.         return retval;  
  238.     }  
  239.       
  240.     private static int fill(InputStream stream, byte[] buffer) {  
  241.         return fill(stream, 0, buffer.length, buffer);  
  242.     }  
  243.       
  244.     /** 
  245.      * 读取流数据 
  246.      * @param stream 
  247.      * @param pos 
  248.      * @param len 
  249.      * @param buffer 
  250.      * @return 
  251.      */  
  252.     private static int fill(InputStream stream, int pos, int len, byte[] buffer) {  
  253.         int readSize = 0;  
  254.         try {  
  255.             readSize = stream.read(buffer, pos, len);  
  256.             if (readSize == -1) {  
  257.                 return -1;  
  258.             }  
  259.             assert readSize == len : String.format("len %d readSize %d", len,  
  260.                     readSize);  
  261.         } catch (IOException e) {  
  262.             e.printStackTrace();  
  263.         }  
  264.   
  265.         return readSize;  
  266.     }  
  267. }  

播放器代码
[cpp] view plain copy
  1. public class VideoPlayerActivity extends Activity implements OnClickListener,  
  2. OnSeekBarChangeListener, OnPreparedListener, OnCompletionListener, OnTouchListener {  
  3.   
  4.     private String url = null;// 播放地址  
  5.     private SurfaceView surfaceView = null;  
  6.     private SurfaceHolder surfaceHolder = null;  
  7.     private MediaPlayer mediaPlayer = null;  
  8.     private SeekBar seekBar = null;// 播放进度条  
  9.     private Button playBtn = null;// 播放  
  10.     private Button pauseBtn = null;// 暂停  
  11.     private TextView tv_current = null;  
  12.     private TextView tv_duration = null;  
  13.     private TextView divideTxt = null;  
  14.     private RelativeLayout controlLayout = null;// 操控界面布局  
  15.     private RelativeLayout mediaPlayerLayout = null;  
  16.     private LayoutParams mediaLayoutParams = null;  
  17.     private LinearLayout loadPrgLayout = null;  
  18.   
  19.     private boolean isPlay = false;// 播放状态  
  20.     private boolean isPause = false;  
  21.     private boolean isShow = true;  
  22.     private boolean isEnd = false;  
  23.     private boolean isStartPlayer = false;  
  24.     private boolean isCustomStyle = false;// 开启自定义seekBar样式  
  25.     private int mDuration = 0;  
  26.     private int mSeekBarMax = 0;  
  27.     private int currentPosition = 0;// 当前播放位置  
  28.     private File mDownloadMP4File = null;  
  29.     private MediaPlayerCallback mCallback = null;  
  30.   
  31.     private static final int playBtnID = 0x001;// 播放按钮id  
  32.     private static final int pauseBtnID = 0x002;// 暂停按钮id  
  33.     private static final int btnLayoutID = 0x003;// 按钮布局层id  
  34.     private static final int clockLayoutID = 0x004;// 计时布局层id  
  35.     private static final int surfaceViewID = 0x005;// surfaceView id  
  36.     /** 分秒 */  
  37.     private static final String DateFormatMS = "mm:ss";  
  38.     private static final int UPDATE_PROGRESS_MSG = 0x011;// 更新进度条和计时  
  39.     private static final int SHOW_CONTROL_MSG = 0x012;// 显示控制  
  40.     private static final int COMPLETION_PLAY_MSG = 0x013;// 播放完成  
  41.   
  42.     @Override  
  43.     protected void onCreate(Bundle savedInstanceState) {  
  44.         super.onCreate(savedInstanceState);  
  45.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  46.         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
  47.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  
  48.                 WindowManager.LayoutParams.FLAG_FULLSCREEN); // 全屏  
  49.         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
  50.   
  51.         // 测试视频地址  
  52.         url = "http://minisite.adsame.com/nodie/v2.mp4";  
  53.         String videoFileName = getRootPath() + getVideo(url);  
  54.           
  55.         mDownloadMP4File = Mp4DownloadUtils.downloadMp4File(url, videoFileName, videoHandler);  
  56.         initPlayer();  
  57.     }  
  58.       
  59.     /** 
  60.      * 获取外部存储根路径 
  61.      * @return 
  62.      */  
  63.     private String getRootPath() {  
  64.         String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath();  
  65.         return rootPath;  
  66.     }  
  67.       
  68.     /** 
  69.      * 获取视频文件名字 
  70.      * @param videoUrl 
  71.      * @return 
  72.      */  
  73.     private String getVideo(String videoUrl) {  
  74.         int startIndex = videoUrl.lastIndexOf("/");  
  75.         return url.substring(startIndex, videoUrl.length());  
  76.     }  
  77.       
  78.     /** 
  79.      * 视频下载时的handler 
  80.      */  
  81.     private Handler videoHandler = new Handler() {  
  82.   
  83.         @Override  
  84.         public void handleMessage(Message msg) {  
  85.             super.handleMessage(msg);  
  86.             switch (msg.what) {  
  87.             // 有MP4的mdat数据时,创建播放器  
  88.             case Mp4DownloadUtils.PLAYER_MP4_MSG:  
  89.                 isStartPlayer = true;  
  90.                 createMediaPlayer(mDownloadMP4File);  
  91.                 break;  
  92.             case Mp4DownloadUtils.DOWNLOAD_MP4_COMPLETION:  
  93.                   
  94.                 break;  
  95.             case Mp4DownloadUtils.DOWNLOAD_MP4_FAIL:  
  96.                   
  97.                 break;  
  98.   
  99.             default:  
  100.                 break;  
  101.             }  
  102.         }  
  103.           
  104.     };  
  105.       
  106.     @SuppressWarnings("deprecation")  
  107.     private void initPlayer() {  
  108.         if (surfaceView == null) {  
  109.             surfaceView = new SurfaceView(this);  
  110.             surfaceView.getHolder()  
  111.                 .setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 不缓冲  
  112.             surfaceView.getHolder().setKeepScreenOn(true);// 保持屏幕高亮  
  113.             surfaceView.getHolder().addCallback(new SurfaceViewCallback());  
  114.             surfaceView.setId(surfaceViewID);  
  115.             surfaceView.setOnClickListener(this);  
  116.             surfaceView.setEnabled(false);  
  117.             surfaceHolder = surfaceView.getHolder();  
  118.         }  
  119.   
  120.         initPlayerButton();  
  121.         RelativeLayout btnLayout = new RelativeLayout(this);  
  122.         btnLayout.setId(btnLayoutID);  
  123.         btnLayout.setGravity(Gravity.CENTER);  
  124.         LayoutParams btnParams = new LayoutParams(LayoutParams.WRAP_CONTENT,  
  125.                 LayoutParams.WRAP_CONTENT);  
  126.         btnParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);  
  127.         btnParams.addRule(RelativeLayout.CENTER_VERTICAL);  
  128.         btnLayout.setLayoutParams(btnParams);  
  129.         btnLayout.addView(playBtn);  
  130.         btnLayout.addView(pauseBtn);  
  131.   
  132.         initClockTextView();  
  133.         LinearLayout clockLayout = new LinearLayout(this);  
  134.         clockLayout.setId(clockLayoutID);  
  135.         clockLayout.setOrientation(LinearLayout.HORIZONTAL);  
  136.         LayoutParams clockTVLayoutParams = new LayoutParams(  
  137.                 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
  138.         clockTVLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);  
  139.         clockTVLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);  
  140.         clockLayout.setLayoutParams(clockTVLayoutParams);  
  141.         clockLayout.addView(tv_duration);  
  142.         clockLayout.addView(divideTxt);  
  143.         clockLayout.addView(tv_current);  
  144.         // 进度条必须在btnLayout和prgLayout后面  
  145.         initProgressSeekBar();  
  146.         // 下载进度  
  147.         initProgressBar();  
  148.   
  149.         controlLayout = new RelativeLayout(this);  
  150.         LayoutParams rlParams = new LayoutParams(LayoutParams.MATCH_PARENT,  
  151.                 LayoutParams.WRAP_CONTENT);  
  152.         rlParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);  
  153.         int marginLeft = 5;  
  154.         int marginRight = 5;  
  155.         rlParams.setMargins(marginLeft, 0, marginRight, 0);  
  156.         controlLayout.setLayoutParams(rlParams);  
  157.         controlLayout.setVisibility(View.GONE);  
  158.         controlLayout.setBackgroundColor(Color.GRAY);  
  159.         controlLayout.addView(btnLayout);  
  160.         controlLayout.addView(seekBar);  
  161.         controlLayout.addView(clockLayout);  
  162.           
  163.         setViewDrawableStyle();  
  164.   
  165.         mediaPlayerLayout = new RelativeLayout(this);  
  166.         mediaLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);  
  167.         mediaLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);  
  168.         mediaPlayerLayout.setLayoutParams(mediaLayoutParams);  
  169.         mediaPlayerLayout.addView(surfaceView);  
  170.         mediaPlayerLayout.addView(controlLayout);  
  171.         mediaPlayerLayout.addView(loadPrgLayout);  
  172.         RelativeLayout layout = new RelativeLayout(this);  
  173.         layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,   
  174.                 LayoutParams.MATCH_PARENT));  
  175.         layout.addView(mediaPlayerLayout);  
  176.         setContentView(layout);  
  177.     }  
  178.   
  179.     private void initPlayerButton() {  
  180.         int btnWidth = 60;  
  181.         int btnHeight = 60;  
  182.         // 开始按钮  
  183.         playBtn = new Button(this);  
  184.         playBtn.setId(playBtnID);  
  185.         playBtn.setOnClickListener(this);  
  186.         LayoutParams playParams = new LayoutParams(btnWidth, btnHeight);  
  187.         playBtn.setLayoutParams(playParams);  
  188.         playBtn.setEnabled(false);  
  189.   
  190.         // 暂停按钮  
  191.         pauseBtn = new Button(this);  
  192.         pauseBtn.setId(pauseBtnID);  
  193.         pauseBtn.setOnClickListener(this);  
  194.         LayoutParams pauseParams = new LayoutParams(btnWidth, btnHeight);  
  195.         pauseBtn.setLayoutParams(pauseParams);  
  196.         pauseBtn.setVisibility(View.GONE);  
  197.     }  
  198.   
  199.     private void initClockTextView() {  
  200.         // 播放进度  
  201.         tv_current = new TextView(this);  
  202.         tv_current.setTextColor(Color.WHITE);  
  203.         tv_current.setText(getStringByFormat(currentPosition, DateFormatMS));  
  204.         // 播放持续时间  
  205.         tv_duration = new TextView(this);  
  206.         tv_duration.setTextColor(Color.WHITE);  
  207.         tv_duration.setText(getStringByFormat(mDuration, DateFormatMS));  
  208.         // 分隔线  
  209.         divideTxt = new TextView(this);  
  210.         divideTxt.setText("/");  
  211.         divideTxt.setTextColor(Color.WHITE);  
  212.     }  
  213.   
  214.     private void initProgressSeekBar() {  
  215.         // 播放进度条  
  216.         seekBar = new SeekBar(this);  
  217.         seekBar.setOnSeekBarChangeListener(this);  
  218.         LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,  
  219.                 LayoutParams.WRAP_CONTENT);  
  220.         params.addRule(RelativeLayout.RIGHT_OF, btnLayoutID);  
  221.         params.addRule(RelativeLayout.LEFT_OF, clockLayoutID);  
  222.         params.addRule(RelativeLayout.CENTER_VERTICAL);  
  223.         int leftpadding = 15;  
  224.         int rightpadding = 15;  
  225.         seekBar.setLayoutParams(params);  
  226.         seekBar.setPadding(leftpadding, 0, rightpadding, 0);  
  227.         seekBar.setOnTouchListener(this);  
  228.     }  
  229.       
  230.     private void initProgressBar() {  
  231.         LayoutParams layoutParams = new LayoutParams(  
  232.                 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
  233.         layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);  
  234.           
  235.         loadPrgLayout = new LinearLayout(this);  
  236.         loadPrgLayout.setLayoutParams(layoutParams);  
  237.         loadPrgLayout.setOrientation(LinearLayout.VERTICAL);  
  238.         loadPrgLayout.setVisibility(View.VISIBLE);  
  239.         loadPrgLayout.setGravity(Gravity.CENTER);  
  240.           
  241.         LayoutParams params = new LayoutParams(  
  242.                 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
  243.         ProgressBar loadPrgBar = new ProgressBar(this);  
  244.         loadPrgBar.setLayoutParams(params);  
  245.         loadPrgLayout.addView(loadPrgBar);  
  246.     }  
  247.   
  248.     /** 
  249.      * 设置view的样式 
  250.      */  
  251.     private void setViewDrawableStyle() {  
  252.         try {  
  253.             // setBackground报错需要Android版本号在16以上  
  254.             playBtn.setBackground(createSelectorDrawable(  
  255.                     getAssetDrawable(this"btn_play_normal.png"),  
  256.                     getAssetDrawable(this"btn_play_pressed.png")));  
  257.             pauseBtn.setBackground(createSelectorDrawable(  
  258.                     getAssetDrawable(this"btn_pause_normal.png"),  
  259.                     getAssetDrawable(this"btn_pause_pressed.png")));  
  260.             if (isCustomStyle) {  
  261.                 seekBar.setThumb(createSeekbarThumbDrawable(  
  262.                         getAssetDrawable(this"progress_thumb_normal.png"),  
  263.                         getAssetDrawable(this"progress_thumb_pressed.png")));  
  264.                 seekBar.setProgressDrawable(createSeekbarProgressDrawable(  
  265.                         getAssetDrawable(this"progress_bg.png"),  
  266.                         getAssetDrawable(this"progress_secondary.png"),   
  267.                         getAssetDrawable(this"progress.png")));  
  268.                 controlLayout.setBackground(getAssetDrawable(this"ctrl_layout_bg.png"));  
  269.             }  
  270.         } catch (Exception e) {  
  271.             e.printStackTrace();  
  272.         }  
  273.     }  
  274.       
  275.     /** 
  276.      * 获取accets目录下的图片 
  277.      * @param context 
  278.      * @param imageName 
  279.      * @return 
  280.      * @throws IOException 
  281.      */  
  282.     public Drawable getAssetDrawable(Context context, String imageName) throws IOException {  
  283.         return BitmapDrawable.createFromStream(context.getAssets().open(imageName), imageName);  
  284.     }  
  285.   
  286.     /** 
  287.      * 创建图片选择器 
  288.      * @param normal 
  289.      * @param pressed 
  290.      * @return 
  291.      */  
  292.     private Drawable createSelectorDrawable(Drawable normal, Drawable pressed) {  
  293.         StateListDrawable drawable = new StateListDrawable();  
  294.         drawable.addState(new int[] { android.R.attr.state_focused },  
  295.                 normal);  
  296.         drawable.addState(new int[] { android.R.attr.state_pressed },  
  297.                 pressed);  
  298.         drawable.addState(new int[] {}, normal);  
  299.         return drawable;  
  300.     }  
  301.   
  302.     /** 
  303.      * 创建seekBar拖动点样式图 
  304.      *  
  305.      * @param normal 
  306.      *            正常 
  307.      * @param pressed 
  308.      *            下压 
  309.      * @return 
  310.      */  
  311.     private Drawable createSeekbarThumbDrawable(Drawable normal, Drawable pressed) {  
  312.         StateListDrawable drawable = new StateListDrawable();  
  313.         drawable.addState(new int[] { android.R.attr.state_pressed }, pressed);  
  314.         drawable.addState(new int[] { android.R.attr.state_focused }, normal);  
  315.         drawable.addState(new int[] {}, normal);  
  316.         return drawable;  
  317.     }  
  318.   
  319.     /** 
  320.      * 创建seekBar进度样式图 
  321.      *  
  322.      * @param background 
  323.      *            背景图 
  324.      * @param secondaryProgress 
  325.      *            第二进度能量图 
  326.      * @param progress 
  327.      *            进度能量图 
  328.      * @return 
  329.      */  
  330.     private Drawable createSeekbarProgressDrawable(Drawable background,  
  331.             Drawable secondaryProgress, Drawable progress) {  
  332.         LayerDrawable progressDrawable = (LayerDrawable) seekBar  
  333.                 .getProgressDrawable();  
  334.         int itmeSize = progressDrawable.getNumberOfLayers();  
  335.         Drawable[] outDrawables = new Drawable[itmeSize];  
  336.         for (int i = 0; i < itmeSize; i++) {  
  337.             switch (progressDrawable.getId(i)) {  
  338.             case android.R.id.background:// 设置进度条背景  
  339.                 outDrawables[i] = background;  
  340.                 break;  
  341.             case android.R.id.secondaryProgress:// 设置二级进度条  
  342.                 outDrawables[i] = secondaryProgress;  
  343.                 break;  
  344.             case android.R.id.progress:// 设置进度条  
  345.                 ClipDrawable oidDrawable = (ClipDrawable) progressDrawable  
  346.                         .getDrawable(i);  
  347.                 Drawable drawable = progress;  
  348.                 ClipDrawable proDrawable = new ClipDrawable(drawable,  
  349.                         Gravity.LEFT, ClipDrawable.HORIZONTAL);  
  350.                 proDrawable.setLevel(oidDrawable.getLevel());  
  351.                 outDrawables[i] = proDrawable;  
  352.                 break;  
  353.             default:  
  354.                 break;  
  355.             }  
  356.         }  
  357.         progressDrawable = new LayerDrawable(outDrawables);  
  358.         return progressDrawable;  
  359.     }  
  360.       
  361.     private class SurfaceViewCallback implements  
  362.             android.view.SurfaceHolder.Callback {  
  363.   
  364.         @Override  
  365.         public void surfaceCreated(SurfaceHolder holder) {  
  366.             createMediaPlayer();  
  367.         }  
  368.   
  369.         @Override  
  370.         public void surfaceChanged(SurfaceHolder holder, int format, int width,  
  371.                 int height) {  
  372.         }  
  373.   
  374.         @Override  
  375.         public void surfaceDestroyed(SurfaceHolder holder) {  
  376.             destroy();  
  377.         }  
  378.     }  
  379.       
  380.     private void createMediaPlayer() {  
  381.         createMediaPlayer(null);  
  382.     }  
  383.       
  384.     private void createMediaPlayer(File videoFile) {  
  385.         if (url == null && videoFile == null && !isStartPlayer) {  
  386.             return;  
  387.         }  
  388.           
  389.         if (mediaPlayer == null) {  
  390.             mediaPlayer = new MediaPlayer();  
  391.         }  
  392.         mediaPlayer.reset();  
  393.         mediaPlayer.setOnCompletionListener(this);  
  394.         mediaPlayer.setOnPreparedListener(this);// 设置监听事件  
  395.         mediaPlayer.setDisplay(surfaceHolder);// 把视频显示到surfaceView上  
  396.         if (videoFile != null) {  
  397.             setDataSource(videoFile);  
  398.         } else {  
  399.             setDataSource(url);  
  400.         }  
  401.         mediaPlayer.prepareAsync();// 准备播放  
  402.     }  
  403.       
  404.     private void setDataSource(String src) {  
  405.         try {  
  406.             if (src.startsWith("http://")) {  
  407.                 mediaPlayer.setDataSource(this, Uri.parse(src));  
  408.             } else {  
  409.                 setDataSource(new File(src));  
  410.             }  
  411.         } catch (IOException e) {  
  412.             e.printStackTrace();  
  413.         }  
  414.     }  
  415.       
  416.     private void setDataSource(File file) {  
  417.         try {  
  418.             FileInputStream fis = new FileInputStream(file);  
  419.             mediaPlayer.setDataSource(fis.getFD());// 设置播放路径  
  420.             fis.close();  
  421.         } catch (IOException e) {  
  422.             e.printStackTrace();  
  423.         }  
  424.     }  
  425.   
  426.     @Override  
  427.     public void onPrepared(MediaPlayer mp) {  
  428.         if (isPause) {  
  429.               
  430.         } else {  
  431.             mediaPlayer.start();  
  432.             playBtn.setEnabled(true);  
  433.             playBtn.setVisibility(View.GONE);  
  434.             pauseBtn.setVisibility(View.VISIBLE);  
  435.               
  436.             if (currentPosition > 0) {  
  437.                 mediaPlayer.seekTo(currentPosition);  
  438.             }  
  439.         }  
  440.         loadPrgLayout.setVisibility(View.GONE);  
  441.         controlLayout.setVisibility(View.VISIBLE);  
  442.         surfaceView.setEnabled(true);  
  443.         mHandler.post(showControlRunnable);  
  444.         mHandler.post(updateProgressRunnable);  
  445.           
  446.         if (mSeekBarMax == 0 || mDuration == 0) {  
  447.             mSeekBarMax = seekBar.getMax();  
  448.             mDuration = mediaPlayer.getDuration();  
  449.             mediaPlayerLayout.setMinimumHeight(520);  
  450.             mediaPlayerLayout.invalidate();  
  451.         }  
  452.     }  
  453.   
  454.     @Override  
  455.     public void onStartTrackingTouch(SeekBar seekBar) {  
  456.         removeHandlerTask();  
  457.     }  
  458.   
  459.     @Override  
  460.     public void onProgressChanged(SeekBar seekBar, int progress,  
  461.             boolean fromUser) {  
  462.         if (fromUser) {  
  463.             int milliseconds = countPosition(progress);  
  464.             tv_current.setText(getStringByFormat(milliseconds, DateFormatMS));  
  465.         }  
  466.     }  
  467.   
  468.     @Override  
  469.     public void onStopTrackingTouch(SeekBar seekBar) {  
  470.         if (mediaPlayer != null) {  
  471.             currentPosition = countPosition(seekBar.getProgress());  
  472.             mediaPlayer.seekTo(currentPosition);  
  473.             restartHandlerTack();  
  474.         }  
  475.     }  
  476.       
  477.     private void start() {  
  478.         if (mediaPlayer != null && isPause) {  
  479.             isPlay = true;  
  480.             isPause = false;  
  481.             if (currentPosition > 0) {  
  482.                 mediaPlayer.seekTo(currentPosition);  
  483.             }  
  484.             mediaPlayer.start();  
  485.             restartHandlerTack();  
  486.             playBtn.setVisibility(View.GONE);  
  487.             pauseBtn.setVisibility(View.VISIBLE);  
  488.         }  
  489.     }  
  490.       
  491.     private void pause() {  
  492.         if (mediaPlayer != null && mediaPlayer.isPlaying()) {  
  493.             isPlay = false;  
  494.             isPause = true;  
  495.             mediaPlayer.pause();  
  496.             playBtn.setVisibility(View.VISIBLE);  
  497.             pauseBtn.setVisibility(View.GONE);  
  498.             controlLayout.setVisibility(View.VISIBLE);  
  499.             currentPosition = mediaPlayer.getCurrentPosition();  
  500.             removeHandlerTask();  
  501.         }  
  502.     }  
  503.       
  504.     private void destroy() {  
  505.         if (mediaPlayer != null && isPlay) {  
  506.             mediaPlayer.pause();  
  507.             controlLayout.setVisibility(View.VISIBLE);  
  508.             currentPosition = mediaPlayer.getCurrentPosition();  
  509.             removeHandlerTask();  
  510.         }  
  511.     }  
  512.       
  513.     @Override  
  514.     public void onClick(View v) {  
  515.         switch (v.getId()) {  
  516.         case playBtnID:// 播放  
  517.             if (mediaPlayer == null) {  
  518.                 createMediaPlayer();  
  519.             } else {  
  520.                 start();  
  521.             }  
  522.             break;  
  523.         case pauseBtnID:// 暂停  
  524.             pause();  
  525.             break;  
  526.         case surfaceViewID:  
  527.             // 先移除任务,如果显示,重新post一次任务  
  528.             mHandler.removeCallbacks(showControlRunnable);  
  529.             if (isShow) {  
  530.                 controlLayout.setVisibility(View.GONE);  
  531.                 isShow = false;  
  532.             } else {  
  533.                 controlLayout.setVisibility(View.VISIBLE);  
  534.                 isShow = true;  
  535.                 mHandler.postDelayed(showControlRunnable, 5000);  
  536.             }  
  537.             break;  
  538.         default:  
  539.             break;  
  540.         }  
  541.     }  
  542.       
  543.     @Override  
  544.     public boolean onTouch(View v, MotionEvent event) {  
  545.         if (v == seekBar && isEnd) {  
  546.             return true;  
  547.         }  
  548.         return false;  
  549.     }  
  550.       
  551.     @Override  
  552.     public void onDetachedFromWindow() {  
  553.         super.onDetachedFromWindow();  
  554.     }  
  555.   
  556.     @Override  
  557.     public void onCompletion(MediaPlayer mp) {  
  558.         // 完成播放  
  559.         try {  
  560.             isEnd = true;  
  561.             isPlay = false;  
  562.             seekBar.setProgress(countProgress(mDuration));  
  563.             tv_current.setText(getStringByFormat(mDuration, DateFormatMS));  
  564.             playBtn.setVisibility(View.VISIBLE);  
  565.             pauseBtn.setVisibility(View.GONE);  
  566.             controlLayout.setVisibility(View.VISIBLE);  
  567.             playBtn.setEnabled(false);  
  568.             removeHandlerTask();  
  569.             mHandler.sendEmptyMessageDelayed(COMPLETION_PLAY_MSG, 500);  
  570.         } catch (Exception e) {  
  571.             e.printStackTrace();  
  572.         }  
  573.     }  
  574.       
  575.     public interface MediaPlayerCallback {  
  576.         public void onCompletion();  
  577.     }  
  578.       
  579.     public void setMediaPlayerCallback(MediaPlayerCallback callback) {  
  580.         mCallback = callback;  
  581.     }  
  582.   
  583.     private Handler mHandler = new Handler() {  
  584.   
  585.         @Override  
  586.         public void handleMessage(Message msg) {  
  587.             super.handleMessage(msg);  
  588.             switch (msg.what) {  
  589.             case UPDATE_PROGRESS_MSG:// 更新播放进度和计时器  
  590.                 if (mediaPlayer != null && mediaPlayer.isPlaying()) {  
  591.                     isPlay = true;  
  592.                     currentPosition = mediaPlayer.getCurrentPosition();  
  593.                     seekBar.setProgress(countProgress(currentPosition));  
  594.                     tv_current  
  595.                             .setText(getStringByFormat(currentPosition, DateFormatMS));  
  596.                     tv_duration.setText(getStringByFormat(mDuration, DateFormatMS));  
  597.                 }  
  598.                 break;  
  599.             case SHOW_CONTROL_MSG:// 显示播放控制控件  
  600.                 if (isPlay) {  
  601.                     isShow = false;  
  602.                     controlLayout.setVisibility(View.GONE);  
  603.                     mHandler.removeCallbacks(showControlRunnable);  
  604.                 } else {  
  605.                     isShow = true;  
  606.                     controlLayout.setVisibility(View.VISIBLE);  
  607.                 }  
  608.                 break;  
  609.             case COMPLETION_PLAY_MSG:// 完成播放  
  610.                 finish();  
  611.                 if (mCallback != null) {  
  612.                     mCallback.onCompletion();  
  613.                 }  
  614.                 Settings.System.getInt(getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);  
  615.                 break;  
  616.   
  617.             default:  
  618.                 break;  
  619.             }  
  620.         }  
  621.   
  622.     };  
  623.       
  624.     /** 
  625.      * 重新启动任务 
  626.      */  
  627.     private void restartHandlerTack() {  
  628.         mHandler.post(updateProgressRunnable);  
  629.         mHandler.postDelayed(showControlRunnable, 5000);  
  630.     }  
  631.       
  632.     /** 
  633.      * 移除任务 
  634.      */  
  635.     private void removeHandlerTask() {  
  636.         mHandler.removeCallbacks(showControlRunnable);  
  637.         mHandler.removeCallbacks(updateProgressRunnable);  
  638.     }  
  639.   
  640.     /** 
  641.      * 更新进度条线程 
  642.      */  
  643.     private Runnable updateProgressRunnable = new Runnable() {  
  644.   
  645.         @Override  
  646.         public void run() {  
  647.             mHandler.sendEmptyMessage(UPDATE_PROGRESS_MSG);  
  648.             mHandler.postDelayed(updateProgressRunnable, 1000);  
  649.         }  
  650.     };  
  651.   
  652.     /** 
  653.      * 显示控制线程 
  654.      */  
  655.     private Runnable showControlRunnable = new Runnable() {  
  656.   
  657.         @Override  
  658.         public void run() {  
  659.             mHandler.sendEmptyMessage(SHOW_CONTROL_MSG);  
  660.             mHandler.postDelayed(showControlRunnable, 5000);  
  661.         }  
  662.     };  
  663.   
  664.     /** 
  665.      * 计算播放进度 
  666.      *  
  667.      * @param position 
  668.      * @return 
  669.      */  
  670.     private int countProgress(int position) {  
  671.         if (mDuration == 0) {  
  672.             return 0;  
  673.         }  
  674.   
  675.         return position * mSeekBarMax / mDuration;  
  676.     }  
  677.   
  678.     /** 
  679.      * 计算播放位置 
  680.      *  
  681.      * @param progress 
  682.      * @return 
  683.      */  
  684.     private int countPosition(int progress) {  
  685.         if (mSeekBarMax == 0) {  
  686.             return 0;  
  687.         }  
  688.   
  689.         return progress * mDuration / mSeekBarMax;  
  690.     }  
  691.   
  692.     /** 
  693.      * 描述:获取milliseconds表示的日期时间的字符串. 
  694.      *  
  695.      * @param milliseconds 
  696.      * @param format 
  697.      *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss" 
  698.      * @return String 日期时间字符串 
  699.      */  
  700.     private String getStringByFormat(long milliseconds, String format) {  
  701.         String thisDateTime = null;  
  702.         try {  
  703.             SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);  
  704.             mSimpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+0"));  
  705.             thisDateTime = mSimpleDateFormat.format(milliseconds);  
  706.         } catch (Exception e) {  
  707.             e.printStackTrace();  
  708.         }  
  709.         return thisDateTime;  
  710.     }  
  711.       
  712.     @Override  
  713.     protected void onDestroy() {  
  714.         super.onDestroy();  
  715.           
  716.         if (mediaPlayer != null) {  
  717.             mediaPlayer.stop();  
  718.             mediaPlayer.release();  
  719.             mediaPlayer = null;  
  720.             currentPosition = 0;  
  721.         }  
  722.         System.gc();  
  723.     }  
  724.       
  725. }  

需要加的权限
[html] view plain copy
  1. <uses-permission android:name="android.permission.INTERNET" />  

0 0