From 160f93b95602412ccdabe7fc6cf285a7777e1965 Mon Sep 17 00:00:00 2001
From: Dana <Dana_Lee1016@126.com>
Date: 星期三, 24 十二月 2025 15:43:01 +0800
Subject: [PATCH] 新增 useTFCard 字段与 setUseTFCard,由两处服务在创建编码器时传入配置。H264EncodeService、H264EncodeService2 均调用了该 setter。 在创建新文件前(非 TF 卡模式)调用 ensureInternalFlashSpaceForH264;若清理后仍不足 800MB,停止文件输出并返回失败,不再在写帧时重复检查。 保留 TF 卡目录的清理逻辑,仅针对 TF 卡路径。

---
 app/src/main/java/com/anyun/h264/H264EncodeService.java  |    1 
 app/src/main/java/com/anyun/h264/util/FileUtil.java      |  146 ++++++++++++++++++++++++++++++++++++++++++++++++
 app/src/main/java/com/anyun/h264/H264Encoder.java        |   18 ++++++
 app/src/main/java/com/anyun/h264/H264EncodeService2.java |    2 
 4 files changed, 167 insertions(+), 0 deletions(-)

diff --git a/app/src/main/java/com/anyun/h264/H264EncodeService.java b/app/src/main/java/com/anyun/h264/H264EncodeService.java
index 195cc7c..d50bca1 100644
--- a/app/src/main/java/com/anyun/h264/H264EncodeService.java
+++ b/app/src/main/java/com/anyun/h264/H264EncodeService.java
@@ -558,6 +558,7 @@
             
             // 璁剧疆 Context锛堢敤浜庢竻鐞� TF 鍗℃枃浠讹級
             h264Encoder.setContext(this);
+            h264Encoder.setUseTFCard(config.useTFCard);
             
             // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
             int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH;
diff --git a/app/src/main/java/com/anyun/h264/H264EncodeService2.java b/app/src/main/java/com/anyun/h264/H264EncodeService2.java
index bd3ebd7..53315d5 100644
--- a/app/src/main/java/com/anyun/h264/H264EncodeService2.java
+++ b/app/src/main/java/com/anyun/h264/H264EncodeService2.java
@@ -289,6 +289,7 @@
             
             // 璁剧疆 Context锛堢敤浜庢竻鐞� TF 鍗℃枃浠讹級
             h264Encoder.setContext(this);
+
             
             // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
             int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH;
@@ -298,6 +299,7 @@
 
             // 鑾峰彇杈撳嚭鏂囦欢鐩綍锛堟牴鎹畊seTFCard閰嶇疆锛�
             boolean useTFCard = config != null && config.useTFCard;
+            h264Encoder.setUseTFCard(useTFCard);
             String outputDir = getOutputFileDirectory(useTFCard);
             
             // 璁剧疆杈撳嚭鏂囦欢鐩綍锛圚264Encoder浼氳嚜鍔ㄧ鐞嗘枃浠跺垱寤猴紝姣忓垎閽熶竴涓枃浠讹級
diff --git a/app/src/main/java/com/anyun/h264/H264Encoder.java b/app/src/main/java/com/anyun/h264/H264Encoder.java
index c5b9bae..bdceadb 100644
--- a/app/src/main/java/com/anyun/h264/H264Encoder.java
+++ b/app/src/main/java/com/anyun/h264/H264Encoder.java
@@ -88,6 +88,7 @@
     // Context 鍜屾竻鐞嗛厤缃�
     private Context context; // Context 瀵硅薄锛岀敤浜庢竻鐞� TF 鍗℃枃浠�
     private long maxH264TotalSizeGB = 100; // 鏈�澶� H264 鏂囦欢鎬诲ぇ灏忥紙GB锛夛紝榛樿 100GB
+    private boolean useTFCard = false; // 鏄惁浣跨敤 TF 鍗¤緭鍑�
 
     // 缃戠粶浼犺緭鎺у埗
     private boolean enableNetworkTransmission = true; // 鏄惁鍚敤TCP/UDP缃戠粶浼犺緭
@@ -174,6 +175,13 @@
      */
     public void setContext(Context context) {
         this.context = context;
+    }
+
+    /**
+     * 璁剧疆鏄惁浣跨敤 TF 鍗�
+     */
+    public void setUseTFCard(boolean useTFCard) {
+        this.useTFCard = useTFCard;
     }
 
     /**
@@ -459,6 +467,16 @@
                 fileOutputStream = null;
             }
             
+            // 濡傛灉浣跨敤鍐呴儴 Flash锛堥潪 TF 鍗★級锛屽湪鍒涘缓鏂版枃浠跺墠妫�鏌ュ苟娓呯悊绌洪棿
+            if (!useTFCard && context != null) {
+                int result = FileUtil.ensureInternalFlashSpaceForH264(context);
+                if (result == -1) {
+                    Timber.e("Insufficient internal flash space (<800MB) even after cleanup, stop file output");
+                    enableFileOutput = false;
+                    return false;
+                }
+            }
+
             // 妫�鏌ュ苟娓呯悊 TF 鍗′笂鐨� h264 鏂囦欢锛堝鏋滈渶瑕侊級
             if (context != null && outputFileDirectory != null && !outputFileDirectory.isEmpty()) {
                 try {
diff --git a/app/src/main/java/com/anyun/h264/util/FileUtil.java b/app/src/main/java/com/anyun/h264/util/FileUtil.java
index b41f232..2f58618 100644
--- a/app/src/main/java/com/anyun/h264/util/FileUtil.java
+++ b/app/src/main/java/com/anyun/h264/util/FileUtil.java
@@ -179,6 +179,137 @@
     }
 
     /**
+     * 妫�鏌ュ唴閮� Flash锛堥潪 TF 鍗★級鍓╀綑绌洪棿锛屽鏋滃皬浜� 800MB锛屽垯鎸夋椂闂撮『搴忓垹闄� h264_*.h264 鏂囦欢
+     * 鐩綍锛歝ontext.getExternalFilesDir(null).getAbsolutePath()
+     * 鏂囦欢鍚嶆牸寮忕ず渚嬶細h264_1735023032000.h264銆乭264_camera2_1735023032000.h264
+     *
+     * 鍒犻櫎瑙勫垯锛�
+     * - 鎸夋枃浠跺悕涓殑鏃堕棿鎴充粠灏忓埌澶э紙瓒婃棭瓒婂厛鍒狅級渚濇鍒犻櫎
+     * - 姣忓垹闄や竴娆″悗閲嶆柊璁$畻鍓╀綑绌洪棿锛岀洿鍒� 鈮� 800MB 鎴栨枃浠跺垹瀹�
+     *
+     * 杩斿洖鍊硷細
+     * - 0锛氭渶缁堝墿浣欑┖闂� 鈮� 800MB 鎴栨棤闇�鍒犻櫎
+     * - -1锛氬垹闄ゅ畬鎵�鏈夌鍚堣鍒欑殑鏂囦欢鍚庯紝鍓╀綑绌洪棿浠嶇劧 < 800MB
+     */
+    public static int ensureInternalFlashSpaceForH264(Context context) {
+        if (context == null) {
+            Timber.w("ensureInternalFlashSpaceForH264: context is null");
+            return 0;
+        }
+
+        File externalDir = context.getExternalFilesDir(null);
+        if (externalDir == null) {
+            Timber.w("ensureInternalFlashSpaceForH264: external files dir is null");
+            return 0;
+        }
+
+        String basePath = externalDir.getAbsolutePath();
+        long minFreeBytes = 800L * 1024L * 1024L; // 800MB
+
+        long freeBytes = getFreeSpaceBytes(basePath);
+        Timber.d("ensureInternalFlashSpaceForH264: freeBytes=%d, minRequired=%d", freeBytes, minFreeBytes);
+
+        if (freeBytes >= minFreeBytes) {
+            // 绌洪棿鍏呰冻锛屾棤闇�澶勭悊
+            return 0;
+        }
+
+        // 鏀堕泦绗﹀悎鍛藉悕瑙勫垯鐨� h264 鏂囦欢
+        File[] files = externalDir.listFiles();
+        if (files == null || files.length == 0) {
+            Timber.w("ensureInternalFlashSpaceForH264: no 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>() {
+            @Override
+            public int compare(H264FileInfo o1, H264FileInfo o2) {
+                return Long.compare(o1.timestamp, o2.timestamp);
+            }
+        });
+
+        int deletedCount = 0;
+        for (H264FileInfo info : h264Files) {
+            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);
+
+            if (f.delete()) {
+                deletedCount++;
+                // 鍒犻櫎鍚庨噸鏂拌幏鍙栧墿浣欑┖闂达紝鏇村噯纭�
+                freeBytes = getFreeSpaceBytes(basePath);
+                Timber.d("ensureInternalFlashSpaceForH264: after delete, freeBytes=%d", freeBytes);
+            } else {
+                Timber.e("ensureInternalFlashSpaceForH264: failed to delete file -> %s", f.getAbsolutePath());
+            }
+        }
+
+        Timber.i("ensureInternalFlashSpaceForH264: deleted %d files, final freeBytes=%d", deletedCount, freeBytes);
+
+        return freeBytes >= minFreeBytes ? 0 : -1;
+    }
+
+    /**
      * 璁$畻鐩綍涓嬫墍鏈� .h264 鏂囦欢鐨勬�诲ぇ灏忥紙瀛楄妭锛�
      */
     private static long calculateH264FilesSize(File dir) {
@@ -211,6 +342,21 @@
     }
 
     /**
+     * 鑾峰彇鎸囧畾璺緞鐨勫墿浣欑┖闂达紙瀛楄妭锛�
+     */
+    private static long getFreeSpaceBytes(String path) {
+        try {
+            StatFs statFs = new StatFs(path);
+            long blockSize = statFs.getBlockSizeLong();
+            long availableBlocks = statFs.getAvailableBlocksLong();
+            return availableBlocks * blockSize;
+        } catch (Exception e) {
+            Timber.e(e, "Error getting free space (bytes) for path: %s", path);
+            return 0;
+        }
+    }
+
+    /**
      * 閫掑綊鍒犻櫎鐩綍鍙婂叾鎵�鏈夊唴瀹�
      */
     private static boolean deleteDirectory(File dir) {

--
Gitblit v1.8.0