複製視頻,並通知系統圖庫更新,瀏覽文件夾,視頻轉換

1、創建文件夾

    /**
     * 判斷目錄是否存在,不存在則判斷是否創建成功
     *
     * @param file 文件
     * @return {@code true}: 存在或創建成功<br>{@code false}: 不存在或創建失敗
     */
    public static boolean createOrExistsDir(File file) {
        // 如果存在,是目錄則返回true,是文件則返回false,不存在則返回是否創建成功
        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
    }

2、複製視頻

 /**
     * 複製單個文件
     *
     * @param oldPath$Name String 原文件路徑+文件名 如:data/user/0/com.test/files/abc.txt
     * @param newPath$Name String 複製後路徑+文件名 如:data/user/0/com.test/cache/abc.txt
     * @return <code>true</code> if and only if the file was copied;
     * <code>false</code> otherwise
     */
    public  static  boolean copyFile(String oldPath$Name, String newPath$Name) {
        try {
            File oldFile = new File(oldPath$Name);
            if (!oldFile.exists()) {
                Log.e("--Method--", "copyFile:  oldFile not exist.");
                return false;
            } else if (!oldFile.isFile()) {
                Log.e("--Method--", "copyFile:  oldFile not file.");
                return false;
            } else if (!oldFile.canRead()) {
                Log.e("--Method--", "copyFile:  oldFile cannot read.");
                return false;
            }

        /* 如果不需要打log,可以使用下面的語句
        if (!oldFile.exists() || !oldFile.isFile() || !oldFile.canRead()) {
            return false;
        }
        */

            FileInputStream fileInputStream = new FileInputStream(oldPath$Name);    //讀入原文件
            FileOutputStream fileOutputStream = new FileOutputStream(newPath$Name);
            byte[] buffer = new byte[1024];
            int byteRead;
            while ((byteRead = fileInputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, byteRead);
            }
            fileInputStream.close();
            fileOutputStream.flush();
            fileOutputStream.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

3、創建文件夾,複製視頻,通知系統圖庫更新

 private void saveVideoToSDCard(File videoFile, String name) {
        String gpname = "123";
        if (TextUtils.isEmpty(gpname)){
            gpname="公棚";
        }
        String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "我的APP" + File.separator + "視頻集錦" + File.separator + "" + gpname;
        LogUtils.i("創建目錄=" + dir);
        File dirFile = new File(dir);
        if (FileUtils.createOrExistsDir(dirFile)) {

            String target = dir + File.separator + "" + name+ ".mp4";
            LogUtils.i("移動文件=" + target);
            if (FileUtils.copyFile(videoFile.getAbsolutePath(), target)) {
//                showShortToast("視頻已保存:" + target);
                LogUtils.i("視頻已保存:" + target);
//                zipVideo(target);
 //通知系統圖片更新
                int duration;
                try {
                    MediaMetadataRetriever mmr = new MediaMetadataRetriever();
                    mmr.setDataSource(target);
                    duration = Integer.parseInt(mmr.extractMetadata
                            (MediaMetadataRetriever.METADATA_KEY_DURATION));
                    AlbumNotifyHelper.insertVideoToMediaStore(this, target, 0, duration);
                } catch (Exception e) {
                    e.printStackTrace();
                    AlbumNotifyHelper.insertVideoToMediaStore(this, target, 0, 0);
                }
                LogUtils.i("移動視頻成功");
              
            } else {
                showShortToast("移動視頻失敗");
            }
        } else {
            showShortToast("創建文件夾失敗");
        }
        refreshFiles();

    }


import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.Log;

import java.io.File;
import java.util.ArrayList;

/**
 * CreateAt : 2017/5/24
 * Describe : 相冊更新通知幫助類
 * 創建時間單位ms
 * 視頻時長單位ms
 *
 */
public class AlbumNotifyHelper {

    public static final String TAG = AlbumNotifyHelper.class.getSimpleName();


    ///////////////////////////////////////////////////////////////////////////
    // 下面是對外公開的重載的方法
    ///////////////////////////////////////////////////////////////////////////

    public static void notifyScanDcim(Context context, String filePath) {
        scanFile(context, filePath);
    }

    public static void insertVideoToMediaStore(Context context, String filePath, long dateTaken, long duration) {
        insertVideoToMediaStore(context, filePath, dateTaken, 0, 0, duration);
    }

 /*   public static void insertVideoToMediaStore(Context context, VideoUtil.VideoInfo videoInfo) {
        insertVideoToMediaStore(context, videoInfo.originalVideoFilePath, videoInfo.dateTaken, videoInfo.width, videoInfo.height, videoInfo.duringTime);
    }*/

    public static void insertImageToMediaStore(Context context, String filePath, long createTime) {
        insertImageToMediaStore(context, filePath, createTime, 0, 0);
    }


    ///////////////////////////////////////////////////////////////////////////
    // 掃描系統相冊核心方法
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 針對系統文夾只需要掃描,不用插入內容提供者,不然會重複
     *
     * @param context  上下文
     * @param filePath 文件路徑
     */
    public static void scanFile(Context context, String filePath) {
        if (!checkFile(filePath))
            return;
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        intent.setData(Uri.fromFile(new File(filePath)));
        context.sendBroadcast(intent);
    }


    ///////////////////////////////////////////////////////////////////////////
    // 非系統相冊像MediaContent中插入數據,核心方法
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 針對非系統文件夾下的文件,使用該方法
     * 插入時初始化公共字段
     *
     * @param filePath 文件
     * @param time     ms
     * @return ContentValues
     */
    private static ContentValues initCommonContentValues(String filePath, long time) {
        ContentValues values = new ContentValues();
        File saveFile = new File(filePath);
        long timeMillis = getTimeWrap(time);
        values.put(MediaStore.MediaColumns.TITLE, saveFile.getName());
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, saveFile.getName());
        values.put(MediaStore.MediaColumns.DATE_MODIFIED, timeMillis);
        values.put(MediaStore.MediaColumns.DATE_ADDED, timeMillis);
        values.put(MediaStore.MediaColumns.DATA, saveFile.getAbsolutePath());
        values.put(MediaStore.MediaColumns.SIZE, saveFile.length());
        return values;
    }

    /**
     * 保存到照片到本地,並插入MediaStore以保證相冊可以查看到,這是更優化的方法,防止讀取的照片獲取不到寬高
     *
     * @param context    上下文
     * @param filePath   文件路徑
     * @param createTime 創建時間 <=0時爲當前時間 ms
     * @param width      寬度
     * @param height     高度
     */
    public static void insertImageToMediaStore(Context context, String filePath, long createTime, int width, int height) {
        if (!checkFile(filePath))
            return;
        createTime = getTimeWrap(createTime);
        ContentValues values = initCommonContentValues(filePath, createTime);
        values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, createTime);
        values.put(MediaStore.Images.ImageColumns.ORIENTATION, 0);
        values.put(MediaStore.Images.ImageColumns.ORIENTATION, 0);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
            if (width > 0) values.put(MediaStore.Images.ImageColumns.WIDTH, 0);
            if (height > 0) values.put(MediaStore.Images.ImageColumns.HEIGHT, 0);
        }
        values.put(MediaStore.MediaColumns.MIME_TYPE, getPhotoMimeType(filePath));
        context.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    }

    /**
     * 保存到視頻到本地,並插入MediaStore以保證相冊可以查看到,這是更優化的方法,防止讀取的視頻獲取不到寬高
     *
     * @param context    上下文
     * @param filePath   文件路徑
     * @param createTime 創建時間 <=0時爲當前時間 ms
     * @param duration   視頻長度 ms
     * @param width      寬度
     * @param height     高度
     */
    public static void insertVideoToMediaStore(Context context, String filePath, long createTime, int width, int height, long duration) {
        if (!checkFile(filePath))
            return;
        createTime = getTimeWrap(createTime);
        ContentValues values = initCommonContentValues(filePath, createTime);
        values.put(MediaStore.Video.VideoColumns.DATE_TAKEN, createTime);
        if (duration > 0)
            values.put(MediaStore.Video.VideoColumns.DURATION, duration);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
            if (width > 0) values.put(MediaStore.Video.VideoColumns.WIDTH, width);
            if (height > 0) values.put(MediaStore.Video.VideoColumns.HEIGHT, height);
        }
        values.put(MediaStore.MediaColumns.MIME_TYPE, getVideoMimeType(filePath));
        context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
    }


    // 是不是系統相冊
    private static boolean isSystemDcim(String path) {
        return path.toLowerCase().contains("dcim") || path.toLowerCase().contains("camera");
    }

    // 獲取照片的mine_type
    private static String getPhotoMimeType(String path) {
        String lowerPath = path.toLowerCase();
        if (lowerPath.endsWith("jpg") || lowerPath.endsWith("jpeg")) {
            return "image/jpeg";
        } else if (lowerPath.endsWith("png")) {
            return "image/png";
        } else if (lowerPath.endsWith("gif")) {
            return "image/gif";
        }
        return "image/jpeg";
    }

    // 獲取video的mine_type,暫時只支持mp4,3gp
    private static String getVideoMimeType(String path) {
        String lowerPath = path.toLowerCase();
        if (lowerPath.endsWith("mp4") || lowerPath.endsWith("mpeg4")) {
            return "video/mp4";
        } else if (lowerPath.endsWith("3gp")) {
            return "video/3gp";
        }
        return "video/mp4";
    }

    // 獲得轉化後的時間
    private static long getTimeWrap(long time) {
        if (time <= 0) {
            return System.currentTimeMillis();
        }
        return time;
    }

    // 檢測文件存在
    private static boolean checkFile(String filePath) {
        //boolean result = FileUtil.fileIsExist(filePath);
        boolean result = false;
        File mFile = new File(filePath);
        if (mFile.exists()) {
            result = true;
        }
        Log.e(TAG, "文件不存在 path = " + filePath);
        return result;
    }


}







4、瀏覽文件夾

private void refreshFiles() {

        String gpname = MasterApplication.getInstance().getCurrentUser().shopname;
        String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "我的APP" + File.separator + "視頻集錦" + File.separator + "" + gpname;
        LogUtils.i("創建目錄=" + dir);
        File dirFile = new File(dir);
        if (FileUtils.createOrExistsDir(dirFile)) {
            List<String> files = FileUtils.getFilesAllName(dir);
            if (ListUtils.isEmpty(files)) {
                return;
            }
            for (int i = 0; i < files.size(); i++) {
                String fileName = files.get(i);
				
                String suffix = fileName.substring(0, fileName.lastIndexOf("."));
                LogUtils.i("文件名=" + fileName);
                LogUtils.i("suffix=" + suffix);
                if (!TextUtils.isEmpty(suffix)) {
                    String[] arrs = suffix.split("/");
                    if (arrs != null && arrs.length > 1) {
                        suffix = arrs[arrs.length - 1];
                        LogUtils.i("文件名=" + suffix);
                        for (int j = 0; j < lists.size(); j++) {
                            if (suffix.equals(lists.get(j).getZh())) {
                                lists.get(j).setLocalVideo(fileName);
                            }
                        }
                    }
                }

            }
            mAdapter.setDataAndRefreshUI(lists);
        } else {
            LogUtils.e("創建文件夾失敗");

        }
    }
    public static List<String> getFilesAllName(String path) {
        File file=new File(path);
        File[] files=file.listFiles();
        if (files == null){Log.e("error","空目錄");return null;}
        List<String> s = new ArrayList<>();
        for(int i =0;i<files.length;i++){
            s.add(files[i].getAbsolutePath());
        }
        return s;
    }

5、必要時還可以對視頻進行壓縮,使用的是VideoCompress:https://github.com/wuxiaoqiang625/VideoCompress

  private void zipVideo(String inputDir) {
        String gpname = MasterApplication.getInstance().getCurrentUser().shopname;
        String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "我的APP" + File.separator + "視頻集錦" + File.separator + "" + gpname;
        LogUtils.i("創建目錄=" + dir);
        File dirFile = new File(dir);
        if (FileUtils.createOrExistsDir(dirFile)) {
            final String destPath = dir + File.separator + "" + zuhuanhao + ".mp4";


//            final String destPath = outputDir + File.separator + "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss", getLocale()).format(new Date()) + ".mp4";

            VideoCompress.compressVideoMedium(inputDir, destPath, new VideoCompress.CompressListener() {
                @Override
                public void onStart() {
                    showProgressDialog("正在壓縮視頻...");
//                    startTime = System.currentTimeMillis();
//                    setTime(startTime, "開始時間");
                    LogUtils.i("壓縮前大小 = " + getFileSize(inputDir));
                }

                @Override
                public void onSuccess() {
                    dismissProgressDialog();
                    showShortToast("視頻已保存:" + destPath);
                    //通知系統更新
                    int duration;
                    try {
                        MediaMetadataRetriever mmr = new MediaMetadataRetriever();
                        mmr.setDataSource(destPath);
                        duration = Integer.parseInt(mmr.extractMetadata
                                (MediaMetadataRetriever.METADATA_KEY_DURATION));
                        AlbumNotifyHelper.insertVideoToMediaStore(KSListActivity.this, destPath, 0, duration);
                    } catch (Exception e) {
                        e.printStackTrace();
                        AlbumNotifyHelper.insertVideoToMediaStore(KSListActivity.this, destPath, 0, 0);
                    }
                    refreshFiles();
//                    endTime = System.currentTimeMillis();
//                    setTime(endTime, "結束時間");
                    LogUtils.i("壓縮後大小 = " + getFileSize(destPath));
//                    openFile(new File(destPath));
                }

                @Override
                public void onFail() {
                    dismissProgressDialog();
                    showShortToast("視頻壓縮失敗");
//                    endTime = System.currentTimeMillis();
//                    setTime(endTime, "失敗時間");
                }

                @Override
                public void onProgress(float percent) {
//                    LogUtils.i("壓縮進度+" + String.valueOf(percent) + "%");
                }
            });
        }
    }

6、壓縮視頻效果並不理想,很模糊,可以使用RxFFmpeg進行格式轉換,mp4轉flv,24mb的視頻可以變成10mb,並且清晰度還不錯。缺點是包內存急速增加。
文章
FFmpeg手撕視頻(Android端)

使用此項目很快捷使用
RxFFmpeg 是基於 ( FFmpeg 4.0 + X264 + mp3lame + fdk-aac ) 編譯的適用於 Android 平臺的音視頻編輯、視頻剪輯的快速處理框架,包含以下功能(視頻拼接,轉碼,壓縮,裁剪,片頭片尾,分離音視頻,變速,添加靜態貼紙和gif動態貼紙,添加字幕,添加濾鏡,添加背景音樂,加速減速視頻,倒放音視頻,音頻裁剪,變聲,混音,圖片合成視頻,視頻解碼圖片,抖音首頁,視頻播放器等主流特色功能

一句代碼:

ffmpeg -i /storage/emulated/0/xxx.mp4 -b:v 4000k -y /storage/emulated/0/xxx.flv

4000k是碼率,根據自己需要調整,值越大越清晰

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章