5个文件已修改
400 ■■■■ 已修改文件
app/src/main/java/com/safeluck/floatwindow/manager/AndroidCameraRecordManager.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/util/FileUtil.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
readMe.md 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/AndroidCameraRecordManager.java
@@ -22,6 +22,7 @@
import com.safeluck.floatwindow.MediaArgu;
import com.safeluck.floatwindow.ResponseVO;
import com.safeluck.floatwindow.util.VideoFileUtils;
import com.safeluck.floatwindow.util.FileUtil;
import timber.log.Timber;
@@ -266,12 +267,15 @@
                    // 停止当前录像
                    stopCurrentRecording();
                    
                    // 通知文件创建
                    // 通知文件创建(当前这段 1 分钟文件)
                    if (currentVideoFile != null) {
                        notifyCallback(2, 0, currentVideoFile.getName());
                    }
                    // 开始新的录像
                    // 每次写入新文件前,检查并清理存储空间(AnYun_VIDEO 下的 mp4)
                    ensureStorageSpaceForMp4();
                    // 开始新的录像(创建下一分钟的新文件)
                    startRecording();
                    
                    // 继续定时
@@ -280,6 +284,41 @@
            }
        }, RECORD_INTERVAL_MS);
    }
    /**
     * 确保存储空间足够(针对 AnYun_VIDEO 下的 mp4)
     * TF 卡:使用 FileUtil.cleanupH264Files(内部已改为清理 mp4)按日期目录删除最早的视频
     * 内部 Flash:使用 FileUtil.ensureInternalFlashSpaceForH264(内部已改为清理 mp4)
     */
    private void ensureStorageSpaceForMp4() {
        if (context == null || mediaArgu == null) {
            return;
        }
        try {
            int tfFlag = mediaArgu.getTfCardFlag(); // 0-内部存储,1-TF 卡
            // 先定位当前使用的日期目录,再取其父目录 AnYun_VIDEO 作为根目录
            File dateDir = VideoFileUtils.getVideoDirectory(context, tfFlag);
            if (dateDir == null) {
                return;
            }
            File rootDir = dateDir.getParentFile(); // .../AnYun_VIDEO
            if (rootDir == null) {
                return;
            }
            String rootPath = rootDir.getAbsolutePath();
            if (tfFlag == 1) {
                // TF 卡:限制总大小 + 剩余空间
                FileUtil.cleanupH264Files(context, rootPath);
            } else {
                // 内部 Flash:确保剩余空间 ≥ 800MB
                FileUtil.ensureInternalFlashSpaceForH264(context);
            }
        } catch (Exception e) {
            Timber.e(e, "ensureStorageSpaceForMp4 error");
        }
    }
    
    /**
     * 停止当前录像
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java
@@ -6,6 +6,7 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.text.TextUtils;
@@ -16,6 +17,7 @@
import com.safeluck.floatwindow.MediaArgu;
import com.safeluck.floatwindow.ResponseVO;
import com.safeluck.floatwindow.util.GlobalData;
import com.safeluck.floatwindow.util.FileUtil;
import com.safeluck.floatwindow.util.VideoFileUtils;
import timber.log.Timber;
@@ -83,6 +85,9 @@
    
    // 录制开始时间(纳秒),用于时间戳同步
    private volatile long recordingStartTimeNs = 0;
    // 刚完成的文件(用于重命名)
    private File completedVideoFile;
    
    /**
     * 录像回调接口
@@ -274,6 +279,13 @@
        
        stopRecordThread();
        releaseResources();
        // 重命名刚完成的文件(停止录像时)
        if (completedVideoFile != null) {
            renameCompletedFile(completedVideoFile);
            completedVideoFile = null;
        }
        if (usbCamera != null) {
            usbCamera.stopCamera();
        }
@@ -336,7 +348,7 @@
            // 创建视频编码器
            MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
            videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 3); // 码率
            videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5); // 码率
            videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
            videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
            
@@ -457,6 +469,11 @@
            mediaMuxer = null;
        }
        
        // 保存刚完成的文件路径,用于后续重命名
        if (currentVideoFile != null && currentVideoFile.exists()) {
            completedVideoFile = currentVideoFile;
        }
        muxerStarted = false;
        videoTrackIndex = -1;
        audioTrackIndex = -1;
@@ -556,8 +573,17 @@
                        
                        // 释放当前资源
                        releaseResources();
                        // 初始化新的编码器和Muxer
                        // 重命名刚完成的文件
                        if (completedVideoFile != null) {
                            renameCompletedFile(completedVideoFile);
                            completedVideoFile = null;
                        }
                        // 每次写入新文件前,检查并清理存储空间(AnYun_VIDEO 下的 mp4)
                        ensureStorageSpaceForMp4();
                        // 初始化新的编码器和Muxer(创建下一分钟的新文件)
                        if (!initEncoderAndMuxer()) {
                            Timber.e("Failed to create new video file");
                            break;
@@ -625,6 +651,13 @@
                }
                
                releaseResources();
                // 重命名刚完成的文件(停止录像时)
                if (completedVideoFile != null) {
                    renameCompletedFile(completedVideoFile);
                    completedVideoFile = null;
                }
                Timber.d("RecordThread ended");
            }
        }
@@ -699,6 +732,126 @@
    }
    
    /**
     * 重命名完成的视频文件
     * 格式:HHmmss_学员名_1或2_时长秒.mp4
     * 例如:132541_学员C_1_58.mp4
     */
    private void renameCompletedFile(File originalFile) {
        if (originalFile == null || !originalFile.exists()) {
            Timber.w("原始文件不存在,无法重命名: %s", originalFile);
            return;
        }
        try {
            // 1. 从原文件名提取时分秒(例如:132541_P1.mp4 -> 132541)
            String originalName = originalFile.getName();
            String timePart = originalName;
            // 移除 .mp4 扩展名
            if (originalName.endsWith(".mp4")) {
                timePart = originalName.substring(0, originalName.length() - 4);
            }
            // 移除 _P1 或 _P2 后缀
            if (timePart.endsWith("_P1") || timePart.endsWith("_P2")) {
                timePart = timePart.substring(0, timePart.length() - 3);
            }
            // 2. 获取学员名字
            String studentName = GlobalData.getInstance().parseWaterMaskInfo("student", "无", GlobalData.ShareType.STRING);
            if (TextUtils.isEmpty(studentName) || "无".equals(studentName)) {
                studentName = "学员";
            }
            // 3. 获取 P1/P2(1 对应 P1,2 对应 P2)
            int cameraId = (mediaArgu != null) ? mediaArgu.getUsbCameraId() : 1;
            String cameraIdStr = String.valueOf(cameraId);
            // 4. 获取视频时长(秒)
            int durationSeconds = getVideoDuration(originalFile);
            // 5. 构建新文件名:HHmmss_学员名_1或2_时长秒.mp4
            String newFileName = String.format("%s_%s_%s_%d.mp4", timePart, studentName, cameraIdStr, durationSeconds);
            File newFile = new File(originalFile.getParent(), newFileName);
            // 6. 重命名文件
            if (originalFile.renameTo(newFile)) {
                Timber.d("文件重命名成功: %s -> %s", originalFile.getName(), newFileName);
                // 更新回调中的文件名
                notifyCallback(2,0,newFileName);
            } else {
                Timber.e("文件重命名失败: %s -> %s", originalFile.getName(), newFileName);
            }
        } catch (Exception e) {
            Timber.e(e, "重命名文件时发生异常: %s", originalFile.getName());
        }
    }
    /**
     * 获取视频文件时长(秒)
     */
    private int getVideoDuration(File videoFile) {
        MediaMetadataRetriever retriever = null;
        try {
            retriever = new MediaMetadataRetriever();
            retriever.setDataSource(videoFile.getAbsolutePath());
            String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
            if (durationStr != null && !durationStr.isEmpty()) {
                long durationMs = Long.parseLong(durationStr);
                int durationSeconds = (int) (durationMs / 1000);
                Timber.d("视频时长: %d 秒", durationSeconds);
                return durationSeconds;
            }
        } catch (Exception e) {
            Timber.e(e, "获取视频时长失败,使用默认值60秒");
        } finally {
            if (retriever != null) {
                try {
                    retriever.release();
                } catch (Exception e) {
                    Timber.e(e, "释放 MediaMetadataRetriever 失败");
                }
            }
        }
        // 如果获取失败,返回默认值60秒
        return 60;
    }
    /**
     * 确保存储空间足够(针对 AnYun_VIDEO 下的 mp4)
     * TF 卡:使用 FileUtil.cleanupH264Files(内部已改为清理 mp4)按日期目录删除最早的视频
     * 内部 Flash:使用 FileUtil.ensureInternalFlashSpaceForH264(内部已改为清理 mp4)
     */
    private void ensureStorageSpaceForMp4() {
        if (context == null || mediaArgu == null) {
            return;
        }
        try {
            int tfFlag = mediaArgu.getTfCardFlag(); // 0-内部存储,1-TF 卡
            // 先定位当前使用的日期目录,再取其父目录 AnYun_VIDEO 作为根目录
            File dateDir = VideoFileUtils.getVideoDirectory(context, tfFlag);
            if (dateDir == null) {
                return;
            }
            File rootDir = dateDir.getParentFile(); // .../AnYun_VIDEO
            if (rootDir == null) {
                return;
            }
            String rootPath = rootDir.getAbsolutePath();
            if (tfFlag == 1) {
                // TF 卡:限制总大小 + 剩余空间
                FileUtil.cleanupH264Files(context, rootPath);
            } else {
                // 内部 Flash:确保剩余空间 ≥ 800MB
                FileUtil.ensureInternalFlashSpaceForH264(context);
            }
        } catch (Exception e) {
            Timber.e(e, "ensureStorageSpaceForMp4 error");
        }
    }
    /**
     * 检查并启动Muxer
     * P1模式:当视频和音频轨道都准备好时启动
     * P2模式:当视频轨道准备好时即可启动(无音频轨道)
app/src/main/java/com/safeluck/floatwindow/util/FileUtil.java
@@ -58,23 +58,24 @@
    }
    /**
     * 清理 TF 卡上的 h264 文件
     * 当 h264 文件总大小超过指定阈值或 TF 卡剩余空间小于指定值时,删除日期最早的文件夹
     *
     * @param context Context 对象,用于获取 TF 卡路径和剩余空间
     * @param h264RootDir h264 根目录路径,例如 "/sdcard/h264"
     * 清理 TF 卡上的 MP4 文件(例如 AnYun_VIDEO 目录)
     * 当 MP4 文件总大小超过指定阈值或 TF 卡剩余空间小于指定值时,删除日期最早的文件夹
     *
     * @param context      Context 对象,用于获取 TF 卡路径和剩余空间
     * @param mp4RootDir   MP4 根目录路径,例如 "/storage/XXXX-XXXX/AnYun_VIDEO"
     * @param maxTotalSizeGB 最大总大小(GB),默认 5GB
     * @param minFreeSpaceGB 最小剩余空间(GB),默认 1GB
     */
    public static void cleanupH264Files(Context context, String h264RootDir, long maxTotalSizeGB, long minFreeSpaceGB) {
        if (context == null || h264RootDir == null || h264RootDir.trim().isEmpty()) {
            Timber.w("Context or h264 root directory is null, skip cleanup");
    public static void cleanupH264Files(Context context, String mp4RootDir, long maxTotalSizeGB, long minFreeSpaceGB) {
        // 注意:为了兼容旧代码,方法名仍然叫 cleanupH264Files,但已经改为清理 MP4 文件
        if (context == null || mp4RootDir == null || mp4RootDir.trim().isEmpty()) {
            Timber.w("Context or mp4 root directory is null, skip cleanup");
            return;
        }
        File h264Root = new File(h264RootDir);
        if (!h264Root.exists() || !h264Root.isDirectory()) {
            Timber.d("H264 root directory does not exist: %s, skip cleanup", h264RootDir);
        File mp4Root = new File(mp4RootDir);
        if (!mp4Root.exists() || !mp4Root.isDirectory()) {
            Timber.d("MP4 root directory does not exist: %s, skip cleanup", mp4RootDir);
            return;
        }
@@ -91,23 +92,24 @@
            Timber.d("TF card free space: %d GB", freeSpaceGB);
            // 扫描所有日期文件夹
            File[] dateDirs = h264Root.listFiles(File::isDirectory);
            File[] dateDirs = mp4Root.listFiles(File::isDirectory);
            if (dateDirs == null || dateDirs.length == 0) {
                Timber.d("No date directories found in h264 root: %s", h264RootDir);
                Timber.d("No date directories found in mp4 root: %s", mp4RootDir);
                return;
            }
            // 按日期排序(最早的在前)
            List<DateDirInfo> dateDirList = new ArrayList<>();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
            // AnYun_VIDEO 使用的是 yyyy_MM_dd 目录名,例如 2026_01_30
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd", Locale.CHINA);
            
            for (File dateDir : dateDirs) {
                String dirName = dateDir.getName();
                // 只处理符合日期格式的文件夹(yyyyMMdd)
                if (dirName.length() == 8 && dirName.matches("\\d{8}")) {
                // 只处理符合日期格式的文件夹(yyyy_MM_dd,例如 2026_01_30)
                if (dirName.matches("\\d{4}_\\d{2}_\\d{2}")) {
                    try {
                        Date date = dateFormat.parse(dirName);
                        long totalSize = calculateH264FilesSize(dateDir);
                        long totalSize = calculateMp4FilesSize(dateDir);
                        dateDirList.add(new DateDirInfo(dateDir, date, totalSize));
                    } catch (ParseException e) {
                        Timber.w("Invalid date directory name: %s", dirName);
@@ -128,7 +130,7 @@
            for (DateDirInfo info : dateDirList) {
                totalSizeGB += info.totalSize / (1024L * 1024L * 1024L);
            }
            Timber.d("Total h264 files size: %d GB, Max allowed: %d GB", totalSizeGB, maxTotalSizeGB);
            Timber.d("Total mp4 files size: %d GB, Max allowed: %d GB", totalSizeGB, maxTotalSizeGB);
            Timber.d("TF card free space: %d GB, Min required: %d GB", freeSpaceGB, minFreeSpaceGB);
            // 检查是否需要清理
@@ -172,19 +174,20 @@
    }
    /**
     * 清理 TF 卡上的 h264 文件(使用默认参数:最大5GB,最小剩余空间1GB)
     * 清理 TF 卡上的 MP4 文件(使用默认参数:最大5GB,最小剩余空间1GB)
     */
    public static void cleanupH264Files(Context context, String h264RootDir) {
        cleanupH264Files(context, h264RootDir, 5, 1);
    public static void cleanupH264Files(Context context, String mp4RootDir) {
        // 为兼容旧调用保留方法名,内部已改为处理 MP4
        cleanupH264Files(context, mp4RootDir, 35, 1);
    }
    /**
     * 检查内部 Flash(非 TF 卡)剩余空间,如果小于 800MB,则按时间顺序删除 h264_*.h264 文件
     * 目录:context.getExternalFilesDir(null).getAbsolutePath()
     * 文件名格式示例:h264_1735023032000.h264、h264_camera2_1735023032000.h264
     * 检查内部 Flash(非 TF 卡)剩余空间,如果小于 800MB,则按时间顺序删除 AnYun_VIDEO 下最早的 MP4 文件
     * 目录结构示例:/sdcard/AnYun_VIDEO/yyMMdd/HHmmss_xxx.mp4
     *
     * 删除规则:
     * - 按文件名中的时间戳从小到大(越早越先删)依次删除
     * - 递归遍历 AnYun_VIDEO 目录,收集所有 .mp4 文件
     * - 按 lastModified 时间从早到晚排序(越早越先删)
     * - 每删除一次后重新计算剩余空间,直到 ≥ 800MB 或文件删完
     *
     * 返回值:
@@ -197,13 +200,21 @@
            return 0;
        }
        File externalDir = context.getExternalFilesDir(null);
        if (externalDir == null) {
            Timber.w("ensureInternalFlashSpaceForH264: external files dir is null");
        // 内部存储根目录(与 VideoFileUtils 中保持一致)
        File externalRoot = android.os.Environment.getExternalStorageDirectory();
        if (externalRoot == null) {
            Timber.w("ensureInternalFlashSpaceForH264: external storage dir is null");
            return 0;
        }
        String basePath = externalDir.getAbsolutePath();
        // AnYun_VIDEO 根目录
        File anyunRoot = new File(externalRoot, "AnYun_VIDEO");
        if (!anyunRoot.exists() || !anyunRoot.isDirectory()) {
            Timber.w("ensureInternalFlashSpaceForH264: AnYun_VIDEO dir not found -> %s", anyunRoot.getAbsolutePath());
            return 0;
        }
        String basePath = anyunRoot.getAbsolutePath();
        long minFreeBytes = 800L * 1024L * 1024L; // 800MB
        long freeBytes = getFreeSpaceBytes(basePath);
@@ -214,85 +225,32 @@
            return 0;
        }
        // 收集符合命名规则的 h264 文件
        File[] files = externalDir.listFiles();
        if (files == null || files.length == 0) {
            Timber.w("ensureInternalFlashSpaceForH264: no files in dir -> %s", basePath);
        // 收集所有 .mp4 文件
        List<File> mp4Files = new ArrayList<>();
        collectMp4Files(anyunRoot, mp4Files);
        if (mp4Files.isEmpty()) {
            Timber.w("ensureInternalFlashSpaceForH264: no mp4 files in dir -> %s", basePath);
            // 已经没有可删的文件,如果仍小于 800MB,则直接返回 -1
            return freeBytes >= minFreeBytes ? 0 : -1;
        }
        class H264FileInfo {
            File file;
            long timestamp;
            H264FileInfo(File file, long timestamp) {
                this.file = file;
                this.timestamp = timestamp;
            }
        }
        List<H264FileInfo> h264Files = new ArrayList<>();
        for (File file : files) {
            if (!file.isFile()) {
                continue;
            }
            String name = file.getName();
            // 只处理 .h264 结尾,且以 h264_ 开头的文件
            if (!name.toLowerCase(Locale.CHINA).endsWith(".h264")) {
                continue;
            }
            if (!name.startsWith("h264_") && !name.startsWith("h264_camera2_")) {
                continue;
            }
            // 提取时间戳部分
            String timePart = null;
            if (name.startsWith("h264_camera2_")) {
                // 前缀长度 13:"h264_camera2_"
                timePart = name.substring("h264_camera2_".length(), name.length() - ".h264".length());
            } else if (name.startsWith("h264_")) {
                // 前缀长度 5:"h264_"
                timePart = name.substring("h264_".length(), name.length() - ".h264".length());
            }
            if (timePart == null || timePart.isEmpty()) {
                continue;
            }
            try {
                long ts = Long.parseLong(timePart);
                h264Files.add(new H264FileInfo(file, ts));
            } catch (NumberFormatException e) {
                // 文件名不符合时间戳格式,跳过
                Timber.w("ensureInternalFlashSpaceForH264: invalid timestamp in file name -> %s", name);
            }
        }
        if (h264Files.isEmpty()) {
            Timber.w("ensureInternalFlashSpaceForH264: no matched h264 files in -> %s", basePath);
            return freeBytes >= minFreeBytes ? 0 : -1;
        }
        // 按时间戳升序排列(越早的越先删)
        Collections.sort(h264Files, new Comparator<H264FileInfo>() {
        // 按 lastModified 升序排列(越早的越先删)
        Collections.sort(mp4Files, new Comparator<File>() {
            @Override
            public int compare(H264FileInfo o1, H264FileInfo o2) {
                return Long.compare(o1.timestamp, o2.timestamp);
            public int compare(File o1, File o2) {
                return Long.compare(o1.lastModified(), o2.lastModified());
            }
        });
        int deletedCount = 0;
        for (H264FileInfo info : h264Files) {
        for (File f : mp4Files) {
            if (freeBytes >= minFreeBytes) {
                break;
            }
            File f = info.file;
            long size = f.length();
            Timber.d("ensureInternalFlashSpaceForH264: deleting file -> %s, size=%d, ts=%d",
                    f.getAbsolutePath(), size, info.timestamp);
            Timber.d("ensureInternalFlashSpaceForH264: deleting mp4 file -> %s, size=%d",
                    f.getAbsolutePath(), size);
            if (f.delete()) {
                deletedCount++;
@@ -310,14 +268,14 @@
    }
    /**
     * 计算目录下所有 .h264 文件的总大小(字节)
     * 计算目录下所有 .mp4 文件的总大小(字节)
     */
    private static long calculateH264FilesSize(File dir) {
    private static long calculateMp4FilesSize(File dir) {
        long totalSize = 0;
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().toLowerCase().endsWith(".h264")) {
                if (file.isFile() && file.getName().toLowerCase().endsWith(".mp4")) {
                    totalSize += file.length();
                }
            }
@@ -342,6 +300,28 @@
    }
    /**
     * 递归收集目录下所有 .mp4 文件
     */
    private static void collectMp4Files(File dir, List<File> outList) {
        if (dir == null || !dir.exists()) {
            return;
        }
        File[] files = dir.listFiles();
        if (files == null) {
            return;
        }
        for (File f : files) {
            if (f.isDirectory()) {
                collectMp4Files(f, outList);
            } else if (f.isFile() && f.getName().toLowerCase(Locale.CHINA).endsWith(".mp4")) {
                outList.add(f);
            }
        }
    }
    /**
     * 获取指定路径的剩余空间(字节)
     */
    private static long getFreeSpaceBytes(String path) {
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java
@@ -49,8 +49,8 @@
            }
        }
        
        // 创建年月日目录(例如:260126)
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd", Locale.getDefault());
        // 创建年月日目录(例如:2026_01_30)
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd", Locale.getDefault());
        String dateDirName = dateFormat.format(new Date());
        File dateDir = new File(videoDir, dateDirName);
        if (!dateDir.exists()) {
readMe.md
@@ -372,4 +372,20 @@
                isServiceBoundState.value = false
                mediaAidlInterface = null
            }
        }
        }
TF 卡录制(tfCardFlag == 1)
目录:<TF 根>/AnYun_VIDEO/yyMMdd/HHmmss_... .mp4
每次新建 1 分钟 MP4 前:
基于 cleanupH264Files(现为 MP4 版本):
按日期目录统计所有 MP4 总大小。
超过 5GB 或 TF 卡剩余空间 < 1GB 时,从最早日期目录开始整目录删除。
内部 Flash 录制(tfCardFlag == 0)
目录:/sdcard/AnYun_VIDEO/yyMMdd/HHmmss_... .mp4
每次新建 1 分钟 MP4 前:
基于 ensureInternalFlashSpaceForH264(现为 MP4 版本):
如果该分区剩余空间 < 800MB:
递归收集 AnYun_VIDEO 下所有 MP4。
按 lastModified 从早到晚依次删,直到 ≥ 800MB 或没有文件。