android中下载视频(包括m3u8)的类

android中凡是网络请求均必须在线程中
权限
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- 以下权限为允许访问移动网络数据信息 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
**/
1.Downloader.java
package com.studio32a.canplayer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
 
public class Downloader {
    public final static int DOWNLOAD_FINISHTED = 1;
    public final static int HASEXCEPTION = 2;
    private final static int INTERVAL = 200;// 请求下载索引的时间间隔200毫秒
    private Context context;
    private long startTime = 0;// 刷新开始计时
    public String saveFullPath;
    private boolean isStopDownload = false;
    public Handler handler;
    private MainActivity mainActivity;
 
    public Downloader(Context context) {
        this.context = context;
        mainActivity = (MainActivity) Downloader.this.context;
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                String inf = (String) msg.obj;
                switch (msg.what) {
                case DOWNLOAD_FINISHTED:
                    if (mainActivity.isRecording) {// 下载全部完成而非手动停止时
                        mainActivity.isRecording = false;
                        mainActivity.iv_rec.setImageResource(R.drawable.recorder);
                        isStopDownload = true;
                    }
                    mainActivity.showInfoDialog("视频保存在"inf);
                    break;
                case HASEXCEPTION:
                    mainActivity.isRecording = false;
                    mainActivity.iv_rec.setImageResource(R.drawable.recorder);
                    isStopDownload = true;
                    mainActivity.showInfoDialog("异常提示""下载失败:\n" + inf);
                    break;
                }
            }
        };
    }
 
    public void setStopDownload(boolean isStopDownload) {
        this.isStopDownload = isStopDownload;
    }
 
    // 下载入口方法
    public void downloadVideo(String saveDir, String srcUrlboolean isM3u8) {
        if (!isM3u8)
            new DownLoadNotM3u8Thread(saveDirsrcUrl).start();
        else
            new DownLoadM3u8SegmentsThread(srcUrlsaveDir).start();
    }
 
    // 发出异常消息
    private void handleException(Exception e) {
        Message message = handler.obtainMessage();
        message.what = HASEXCEPTION;
        message.obj = e.getMessage();
        handler.sendMessage(message);
    }
 
    // =========m3u8视频的下载方法及线程=========
    // 下载索引文件
    private String getIndexFile(String srcUrlthrows Exception {
        URL url = new URL(srcUrl);
        InputStream is = url.openStream();
        InputStreamReader isr = new InputStreamReader(is"UTF-8");
        BufferedReader br = new BufferedReader(isr);
        String content = "";
        String line = null;
        while ((line = br.readLine()) != null) {
            content += line + "\n";
        }
        br.close();
        return content;
    }
 
    // 解析索引文件,得到首片段的路径,可能是长路径,也可能是短径
    private String getFirstSegmentPath(String srcUrlthrows Exception {
        String newfirstSegmentPath = "";        
        String content = getIndexFile(srcUrl);
        Pattern pattern_ts = Pattern.compile(".*ts");// 正则中.不包括\n,因此可分别匹配多行
        Matcher matcher_ts = pattern_ts.matcher(content);
        Pattern pattern_m3u8 = Pattern.compile(".*m3u8.*");// 正则中.不包括\n,因此可分别匹配多行
        Matcher matcher_m3u8 = pattern_m3u8.matcher(content);
        
        if (matcher_ts.find()) {
            newfirstSegmentPath = matcher_ts.group();
            // 在形如/live/cctv2_2中取出/live
            int splitIndex = newfirstSegmentPath.indexOf(File.separator, 1);// 从第二个字符开始找
            String splitStr = "";
            String prePath="";
            if (splitIndex == -1)
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(File.separator) + 1);
            else {
                splitStr = newfirstSegmentPath.substring(0, splitIndex);
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(splitStr));
            }
            // 如果不是完整路径则补全
            if (!newfirstSegmentPath.contains("://")) {
                newfirstSegmentPath = prePath + newfirstSegmentPath;
            }    
            if(!newfirstSegmentPath.contains("://"))newfirstSegmentPath="http://"+newfirstSegmentPath;
        } else if (matcher_m3u8.find()) {
            String m3u8Path = matcher_m3u8.group();
            // 在形如/live/cctv2_2中取出/live
            int splitIndex = m3u8Path.indexOf(File.separator, 1);// 从第二个字符开始找
            String splitStr = "";
            String prePath="";
            if (splitIndex == -1) {
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(File.separator) + 1);
            } else {
                splitStr = m3u8Path.substring(0, splitIndex);
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(splitStr));
            }
            if (!m3u8Path.contains("://")) {
                m3u8Path = prePath + m3u8Path;
            }
            if(!m3u8Path.contains("://"))m3u8Path="http://"+m3u8Path;
            newfirstSegmentPath = getFirstSegmentPath(m3u8Path);
            if(!newfirstSegmentPath.contains("://"))newfirstSegmentPath="http://"+newfirstSegmentPath;
        }        
        return newfirstSegmentPath;
    }    
 
    // 解析索引文件,得到多个片段的路径的列表,可能是长路径,也可能是短径
    private ArrayList<String> getSegmentPathList(String srcUrlthrows Exception {    
        ArrayList<String> list = new ArrayList<String>();
        String content = getIndexFile(srcUrl);
        Pattern pattern_ts = Pattern.compile(".*ts");// 正则中.不包括\n,因此可分别匹配多行
        Matcher matcher_ts = pattern_ts.matcher(content);
        Pattern pattern_m3u8 = Pattern.compile(".*m3u8.*");// 正则中.不包括\n,因此可分别匹配多行
        Matcher matcher_m3u8 = pattern_m3u8.matcher(content);
 
        while (matcher_ts.find()) {
            String newfirstSegmentPath = matcher_ts.group();
            // 在形如/live/cctv2_2中取出/live
            int splitIndex = newfirstSegmentPath.indexOf(File.separator, 1);// 从第二个字符开始找
            String splitStr = "";
            String prePath="";
            if (splitIndex == -1)
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(File.separator) + 1);
            else {
                splitStr = newfirstSegmentPath.substring(0, splitIndex);
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(splitStr));
            }
            // 如果不是完整路径则补全
            if (!newfirstSegmentPath.contains("://")) {
                newfirstSegmentPath = prePath + newfirstSegmentPath;
            }    
            if(!newfirstSegmentPath.contains("://"))newfirstSegmentPath="http://"+newfirstSegmentPath;
            list.add(newfirstSegmentPath);
        }
        if (matcher_m3u8.find()) {
            String m3u8Path = matcher_m3u8.group();
            // 在形如/live/cctv2_2中取出/live
            int splitIndex = m3u8Path.indexOf(File.separator, 1);// 从第二个字符开始找
            String splitStr = "";
            String prePath="";
            if (splitIndex == -1) {
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(File.separator) + 1);
            } else {
                splitStr = m3u8Path.substring(0, splitIndex);
                prePath = srcUrl.substring(0, srcUrl.lastIndexOf(splitStr));
            }
            if (!m3u8Path.contains("://")) {
                m3u8Path = prePath + m3u8Path;
            }
            if(!m3u8Path.contains("://"))m3u8Path="http://"+m3u8Path;            
            list = getSegmentPathList(m3u8Path);
        }
        return list;
 
    }
 
    // 下载单个片段到流,(凡是请求网络均必须在线程中)
    private InputStream getM3u8Instream(String segmentFullUrl) {
        InputStream in = null;
        try {
            URL url = new URL(segmentFullUrl);
            in = url.openStream();
            //Log.i("luo", "成功:" + segmentFullUrl);
        } catch (Exception e) {
            handleException(e);
        }
        return in;
    }
 
    public class DownLoadM3u8SegmentsThread extends Thread {
        private String saveDir;
        private String srcUrl;
 
        public DownLoadM3u8SegmentsThread(String srcUrl, String saveDir) {
            this.srcUrl = srcUrl;
            this.saveDir = saveDir;
        }
 
        // 合并视频片段(循环获取)
        public boolean mergeVideo(String savePaththrows Exception {
            String newfirstSegmentPath = getFirstSegmentPath(srcUrl);
            if (newfirstSegmentPath.isEmpty()) {
                return false;
            }
            String oldfirstSegmentPathath = "";
 
            FileOutputStream fos = new FileOutputStream(savePathtrue);// 采用追加模式
            byte[] bytes = new byte[1024 * 5];
            int length = -1;
            while (!isStopDownload) {
                if (!oldfirstSegmentPathath.equals(newfirstSegmentPath)) {
                    oldfirstSegmentPathath = newfirstSegmentPath;
                    if (!newfirstSegmentPath.isEmpty()) {
                        InputStream in = getM3u8Instream(newfirstSegmentPath);
                        while ((length = in.read(bytes)) != -1) {
                            fos.write(bytes, 0, length);
                        }
                        in.close();
                        startTime = SystemClock.uptimeMillis();
                    }
 
                }
                 Thread.sleep(INTERVAL);// 每间隔INTERVAL毫秒请求一次
                // 已经有多长时间没有得到新的索引
                long noUpdateTimeLong = SystemClock.uptimeMillis() - startTime;
                // 如果1分钟没有得到新索引,就认为已没有新的索引,将剩余索引下载完就停止下载
                if (noUpdateTimeLong > 1000 * 60) {
                    // 下载将剩余的片段
                    ArrayList<String> segmentPathList = getSegmentPathList(srcUrl);
                    for (int i = 1; i < segmentPathList.size(); i++) {// 注意:0已在之前下载(一直未更新),所以从1开始
                        String newSegmentPath = segmentPathList.get(i);
                        if (newSegmentPath != null && !newSegmentPath.isEmpty()) {
                            InputStream in2 = getM3u8Instream(newSegmentPath);
                            while ((length = in2.read(bytes)) != -1) {
                                fos.write(bytes, 0, length);
                            }
                            in2.close();
                        }
                    }
                    isStopDownload = true;
                    break;
                }
                // 每下载一个,更新
                newfirstSegmentPath = getFirstSegmentPath(srcUrl);
            }
            fos.flush();
            fos.close();
            return true;
        }
 
        @Override
        public void run() {
            try {
                // 获取时间截及路径
                SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");// SSS为毫秒
                Date curDate = new Date(System.currentTimeMillis());// 获取当前时间
                String timeStr = formatter.format(curDate);
                String savePath = saveDir + File.separator + timeStr + ".ts";
                Downloader.this.saveFullPath = savePath;
 
                boolean isOk = mergeVideo(savePath);
 
                // 合并函数运行完,即意味着手动停止、程序退出、
                // 进入后台超时、或真正下载完毕,已保存完毕,此时发消息
                Message message = handler.obtainMessage();
                message.what = DOWNLOAD_FINISHTED;
                if (isOk) {
                    message.obj = savePath;
                } else {
                    message.obj = "录制未成功";
                }
                handler.sendMessage(message);
            } catch (Exception e) {
                Log.i("luo"e.getMessage());
                handleException(e);
            }
        }
    }
 
    // =========m3u8之外的普通视频的下载线程=========
    public class DownLoadNotM3u8Thread extends Thread {
        private String savePath;
        private String srcUrl;
 
        public DownLoadNotM3u8Thread(String saveDir, String srcUrl) {
            // 获取时间截
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");// SSS为毫秒
            Date curDate = new Date(System.currentTimeMillis());// 获取当前时间
            String timeStr = formatter.format(curDate);
 
            this.savePath = saveDir + File.separator + timeStr + ".mp4";
            Downloader.this.saveFullPath = savePath;
            this.srcUrl = srcUrl;
        }
 
        @Override
        public void run() {
            try {
                URL url = new URL(srcUrl);
                InputStream in = url.openStream();
                BufferedInputStream bis = new BufferedInputStream(in);
                FileOutputStream fos = new FileOutputStream(savePath);
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                byte[] bytes = new byte[1024 * 8];
                int len = -1;
 
                while ((len = bis.read(bytes)) != -1 && !isStopDownload) {
                    bos.write(bytes, 0, len);
                }
 
                bos.flush();
                bos.close();// 关闭输出流
                bis.close();// 关闭输入流
 
                Message message = handler.obtainMessage();
                message.what = DOWNLOAD_FINISHTED;
                message.obj = savePath;
                handler.sendMessage(message);
 
            } catch (Exception e) {
                handleException(e);
            }
        }
    }
}
2.在MainActivity中
(1)新建Downloader对象,并在下载前设置downloader.setStopDownload(false);,调用其入口方法外
(2)在相关情形下,比如打开新视频时、点击停录按钮,ownloader.setStopDownload(false)设停止下载
(3)退出程序、进入后台的处理方法
①handler
private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {      
        case STOP_DOWNLOAD_DELAY:
            downloader.setStopDownload(true);
            break;
        }
    };
};
视频下载停止延迟,参数如果为-1则移除停止消息下载
private void recordDelayStop(long delayMillis){
    if(delayMillis==-1){
        handler.removeMessages(STOP_DOWNLOAD_DELAY);
    }else{
        handler.sendEmptyMessageDelayed(STOP_DOWNLOAD_DELAYdelayMillis);
    }
}
③onDestroy/onStop/onResume中
 
@Override
protected void onResume() {
    super.onResume();
   
    if(isRecording)recordDelayStop(-1);//移除停止录播的消息
}
 
@Override
protected void onStop() {
    super.onStop();
    if(isRecording)recordDelayStop(1000*60*5);//5分钟后如不恢复将停止录播
}
 
@Override
protected void onDestroy() {
    super.onDestroy();
   
    if(isRecording){//立即停止下载
        recordDelayStop(0);
    }
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值