From 30678653058f62beda7ac3f3ac94bc830b6db85e Mon Sep 17 00:00:00 2001
From: Dana <Dana_Lee1016@126.com>
Date: 星期三, 03 十二月 2025 10:49:35 +0800
Subject: [PATCH] 1.新开进程camera2 2.未测试验证 H264EncodeService2

---
 app/src/main/java/com/anyun/h264/H264EncodeService.java  |  176 ++++++++++
 app/src/main/AndroidManifest.xml                         |   13 
 app/src/main/java/com/anyun/h264/H264EncodeService2.java |  720 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 895 insertions(+), 14 deletions(-)

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 58a629f..573872b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -37,7 +37,7 @@
             </intent-filter>
         </activity>
         
-        <!-- H264缂栫爜鏈嶅姟 -->
+        <!-- H264缂栫爜鏈嶅姟锛堢涓�涓憚鍍忓ご锛屼富杩涚▼锛� -->
         <service
             android:name=".H264EncodeService"
             android:enabled="true"
@@ -46,6 +46,17 @@
                 <action android:name="com.anyun.h264.H264EncodeService" />
             </intent-filter>
         </service>
+        
+        <!-- H264缂栫爜鏈嶅姟2锛堢浜屼釜鎽勫儚澶达紝鐙珛杩涚▼锛� -->
+        <service
+            android:name=".H264EncodeService2"
+            android:enabled="true"
+            android:exported="true"
+            android:process=":camera2">
+            <intent-filter>
+                <action android:name="com.anyun.h264.H264EncodeService2" />
+            </intent-filter>
+        </service>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/com/anyun/h264/H264EncodeService.java b/app/src/main/java/com/anyun/h264/H264EncodeService.java
index 67ce31f..a798418 100644
--- a/app/src/main/java/com/anyun/h264/H264EncodeService.java
+++ b/app/src/main/java/com/anyun/h264/H264EncodeService.java
@@ -1,7 +1,10 @@
 package com.anyun.h264;
 
 import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.RemoteException;
 import timber.log.Timber;
@@ -19,6 +22,8 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * H264缂栫爜鏈嶅姟
@@ -31,6 +36,11 @@
     private H264FileTransmitter h264FileTransmitter; // H264鏂囦欢浼犺緭鍣�
     private String outputFileDirectory; // H264鏂囦欢杈撳嚭鐩綍
     private WatermarkInfo currentWatermarkInfo; // 褰撳墠姘村嵃淇℃伅
+    
+    // 澶氳繘绋嬫敮鎸侊細绗簩涓憚鍍忓ご鐨勬湇鍔¤繛鎺�
+    private IH264EncodeService camera2Service;
+    private ServiceConnection camera2Connection;
+    private boolean isCamera2Bound = false;
     
     // 榛樿缂栫爜鍙傛暟
     private static final int DEFAULT_WIDTH = 640;
@@ -91,6 +101,18 @@
         // 鍋滄骞堕噴鏀剧紪鐮佸櫒鍜屾枃浠朵紶杈撳櫒
         stopEncoder();
         stopFileTransmitter();
+        
+        // 瑙g粦绗簩涓繘绋嬬殑鏈嶅姟
+        if (isCamera2Bound && camera2Connection != null) {
+            try {
+                unbindService(camera2Connection);
+            } catch (Exception e) {
+                Timber.e(e, "Error unbinding camera2 service");
+            }
+            isCamera2Bound = false;
+            camera2Service = null;
+            camera2Connection = null;
+        }
     }
     
     /**
@@ -103,6 +125,7 @@
         int height;
         int framerate;
         String simPhone;
+        Integer cameraId; // 鎽勫儚澶碔D锛�1鎴�2锛岀敤浜庡杩涚▼鏂规锛�
         
         // 浠嶫SON瑙f瀽閰嶇疆
         static EncodeConfig fromJson(String jsonConfig) throws JSONException {
@@ -115,6 +138,7 @@
                 config.ip = null;
                 config.port = 0;
                 config.simPhone = null;
+                config.cameraId = 1; // 榛樿浣跨敤绗竴涓憚鍍忓ご
                 return config;
             }
             
@@ -125,6 +149,13 @@
             config.ip = json.optString("ip", null);
             config.port = json.optInt("port", 0);
             config.simPhone = json.optString("simPhone", null);
+            
+            // 瑙f瀽cameraId锛堝鏋滄湭鎸囧畾锛岄粯璁や负1锛�
+            if (json.has("cameraId")) {
+                config.cameraId = json.optInt("cameraId", 1);
+            } else {
+                config.cameraId = 1; // 榛樿浣跨敤绗竴涓憚鍍忓ご
+            }
             
             return config;
         }
@@ -171,15 +202,34 @@
      *               4-寮�濮嬩紶杈揌264鏂囦欢锛堜粠鏂囦欢璇诲彇骞剁綉缁滄帹閫侊級锛�
      *               5-鍋滄H264鏂囦欢浼犺緭
      * @param jsonConfig JSON鏍煎紡鐨勯厤缃弬鏁�
-     *                   action 0/2: 鍖呭惈锛歩p銆乸ort銆亀idth銆乭eight銆乫ramerate銆乻imPhone
+     *                   action 0/2: 鍖呭惈锛歩p銆乸ort銆亀idth銆乭eight銆乫ramerate銆乻imPhone銆乧ameraId锛堝彲閫夛紝1鎴�2锛岄粯璁�1锛�
      *                   action 4: 鍖呭惈锛歩p銆乸ort銆乫ramerate銆乻imPhone銆乫ilePath銆乸rotocolType锛堝彲閫夛紝1-UDP锛�2-TCP锛岄粯璁CP锛�
-     *                   action 1/3/5: 姝ゅ弬鏁板彲涓虹┖鎴杗ull
+     *                   action 1/3/5: 姝ゅ弬鏁板彲涓虹┖鎴杗ull锛屾垨鍖呭惈cameraId鏉ユ寚瀹氳鍋滄鐨勬憚鍍忓ご
      * @return 0-鎴愬姛锛�1-澶辫触
      */
     private synchronized int controlEncode(int action, String jsonConfig) {
         Timber.d("controlEncode called with action: %d, jsonConfig: %s", action, jsonConfig);
         
         try {
+            // 瑙f瀽cameraId锛堝鏋滈厤缃腑鏈夛級
+            Integer cameraId = null;
+            if (jsonConfig != null && !jsonConfig.trim().isEmpty()) {
+                try {
+                    JSONObject json = new JSONObject(jsonConfig);
+                    if (json.has("cameraId")) {
+                        cameraId = json.optInt("cameraId", 1);
+                    }
+                } catch (JSONException e) {
+                    // 蹇界暐瑙f瀽閿欒锛岀户缁娇鐢ㄥ綋鍓嶈繘绋�
+                }
+            }
+            
+            // 濡傛灉鎸囧畾浜哻ameraId=2锛岃矾鐢卞埌绗簩涓繘绋�
+            if (cameraId != null && cameraId == 2) {
+                return controlEncodeInProcess2(action, jsonConfig);
+            }
+            
+            // 鍚﹀垯鍦ㄥ綋鍓嶈繘绋嬶紙cameraId=1锛夊鐞�
             switch (action) {
                 case 0: // 寮�鍚痟264鏂囦欢鍐欏叆
                     try {
@@ -191,6 +241,10 @@
                     }
                     
                 case 1: // 鍋滄h264缂栫爜骞跺仠姝㈠啓鍏ユ枃浠�
+                    // 妫�鏌ユ槸鍚︽寚瀹氫簡cameraId=2
+                    if (cameraId != null && cameraId == 2) {
+                        return controlEncodeInProcess2(action, jsonConfig);
+                    }
                     return stopEncoder();
                     
                 case 2: // 寮�鍚綉缁滄帹閫乭264锛堜笉鍐欏叆鏂囦欢锛�
@@ -203,6 +257,10 @@
                     }
                     
                 case 3: // 鍋滄h264缂栫爜骞跺仠姝㈢綉缁滄帹閫�
+                    // 妫�鏌ユ槸鍚︽寚瀹氫簡cameraId=2
+                    if (cameraId != null && cameraId == 2) {
+                        return controlEncodeInProcess2(action, jsonConfig);
+                    }
                     return stopEncoder();
                     
                 case 4: // 寮�濮嬩紶杈揌264鏂囦欢
@@ -216,6 +274,10 @@
                     
                 case 5: // 鍋滄H264鏂囦欢浼犺緭
                     Timber.i("瀹㈡埛绔姹傚仠姝㈣棰戞枃浠朵笂浼�");
+                    // 妫�鏌ユ槸鍚︽寚瀹氫簡cameraId=2
+                    if (cameraId != null && cameraId == 2) {
+                        return controlEncodeInProcess2(action, jsonConfig);
+                    }
                     return stopFileTransmitter();
                     
                 default:
@@ -226,6 +288,85 @@
             Timber.e(e, "Error in controlEncode");
             return 1; // 澶辫触
         }
+    }
+    
+    /**
+     * 鍦ㄧ浜屼釜杩涚▼锛坈amera2锛変腑鎵ц缂栫爜鎺у埗
+     */
+    private int controlEncodeInProcess2(int action, String jsonConfig) {
+        Timber.d("Routing to process 2 (camera2) for action: %d", action);
+        
+        try {
+            // 纭繚绗簩涓繘绋嬬殑鏈嶅姟宸茬粦瀹�
+            if (!ensureCamera2ServiceBound()) {
+                Timber.e("Failed to bind camera2 service");
+                return 1;
+            }
+            
+            // 璋冪敤绗簩涓繘绋嬬殑鏈嶅姟
+            if (camera2Service != null) {
+                return camera2Service.controlEncode(action, jsonConfig);
+            } else {
+                Timber.e("Camera2 service is null");
+                return 1;
+            }
+        } catch (RemoteException e) {
+            Timber.e(e, "Error calling camera2 service");
+            return 1;
+        }
+    }
+    
+    /**
+     * 纭繚绗簩涓繘绋嬬殑鏈嶅姟宸茬粦瀹�
+     */
+    private synchronized boolean ensureCamera2ServiceBound() {
+        if (isCamera2Bound && camera2Service != null) {
+            return true;
+        }
+        
+        Timber.d("Binding to camera2 service...");
+        
+        final CountDownLatch latch = new CountDownLatch(1);
+        final boolean[] success = {false};
+        
+        camera2Connection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Timber.d("Camera2 service connected");
+                camera2Service = IH264EncodeService.Stub.asInterface(service);
+                isCamera2Bound = true;
+                success[0] = true;
+                latch.countDown();
+            }
+            
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                Timber.w("Camera2 service disconnected");
+                camera2Service = null;
+                isCamera2Bound = false;
+            }
+        };
+        
+        Intent intent = new Intent(this, H264EncodeService2.class);
+        boolean bound = bindService(intent, camera2Connection, Context.BIND_AUTO_CREATE);
+        
+        if (!bound) {
+            Timber.e("Failed to bind camera2 service");
+            return false;
+        }
+        
+        // 绛夊緟鏈嶅姟杩炴帴锛堟渶澶�5绉掞級
+        try {
+            if (!latch.await(5, TimeUnit.SECONDS)) {
+                Timber.e("Timeout waiting for camera2 service connection");
+                return false;
+            }
+        } catch (InterruptedException e) {
+            Timber.e(e, "Interrupted while waiting for camera2 service");
+            return false;
+        }
+        
+        return success[0];
     }
     
     /**
@@ -245,9 +386,9 @@
             h264Encoder = new H264Encoder();
             
             // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
-            int width = config != null ? config.width : DEFAULT_WIDTH;
-            int height = config != null ? config.height : DEFAULT_HEIGHT;
-            int framerate = config != null ? config.framerate : DEFAULT_FRAME_RATE;
+            int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH;
+            int height = config != null && config.height > 0 ? config.height : DEFAULT_HEIGHT;
+            int framerate = config != null && config.framerate > 0 ? config.framerate : DEFAULT_FRAME_RATE;
             h264Encoder.setEncoderParams(width, height, framerate, DEFAULT_BITRATE);
 
             long timeFile = System.currentTimeMillis()/1000*1000;//Date鏄锛屾墍浠ヤ负浜嗚窡涓嬪彂鐨凞ate starttime涓�鑷达紝姝ゅ闄や互1000 绉�
@@ -264,8 +405,14 @@
             h264Encoder.setEnableNetworkTransmission(false);
             
             // 鍒濆鍖栧苟鍚姩锛堜娇鐢ㄩ厤缃腑鐨勫垎杈ㄧ巼锛�
+            // 鏍规嵁cameraId閫夋嫨鎽勫儚澶磋寖鍥�
+            int[] cameraIdRange = DEFAULT_CAMERA_ID_RANGE;
+            if (config != null && config.cameraId != null) {
+                // 濡傛灉鎸囧畾浜哻ameraId锛屼娇鐢ㄥ搴旂殑鎽勫儚澶�
+                cameraIdRange = new int[]{config.cameraId, config.cameraId};
+            }
             int[] resolution = {width, height};
-            if (h264Encoder.initialize(DEFAULT_CAMERA_ID_RANGE, null, resolution, false)) {
+            if (h264Encoder.initialize(cameraIdRange, null, resolution, false)) {
                 // 搴旂敤宸蹭繚瀛樼殑姘村嵃淇℃伅锛堝鏋滄湁锛�
                 if (currentWatermarkInfo != null) {
                     h264Encoder.setWatermarkInfo(currentWatermarkInfo);
@@ -310,12 +457,9 @@
             h264Encoder = new H264Encoder();
             
             // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
-
-
-            // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
-            int width =  DEFAULT_WIDTH;
-            int height =  DEFAULT_HEIGHT;
-            int framerate = DEFAULT_FRAME_RATE;
+            int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH;
+            int height = config != null && config.height > 0 ? config.height : DEFAULT_HEIGHT;
+            int framerate = config != null && config.framerate > 0 ? config.framerate : DEFAULT_FRAME_RATE;
             h264Encoder.setEncoderParams(width, height, framerate, DEFAULT_BITRATE);
 
             long timeFile = System.currentTimeMillis()/1000*1000;
@@ -339,8 +483,14 @@
             h264Encoder.setProtocolParams(simPhone, (byte)1);
             
             // 鍒濆鍖栧苟鍚姩锛堜娇鐢ㄩ厤缃腑鐨勫垎杈ㄧ巼锛�
+            // 鏍规嵁cameraId閫夋嫨鎽勫儚澶磋寖鍥�
+            int[] cameraIdRange = DEFAULT_CAMERA_ID_RANGE;
+            if (config != null && config.cameraId != null) {
+                // 濡傛灉鎸囧畾浜哻ameraId锛屼娇鐢ㄥ搴旂殑鎽勫儚澶�
+                cameraIdRange = new int[]{config.cameraId, config.cameraId};
+            }
             int[] resolution = {width, height};
-            if (h264Encoder.initialize(DEFAULT_CAMERA_ID_RANGE, null, resolution, false)) {
+            if (h264Encoder.initialize(cameraIdRange, null, resolution, false)) {
                 // 搴旂敤宸蹭繚瀛樼殑姘村嵃淇℃伅锛堝鏋滄湁锛�
                 if (currentWatermarkInfo != null) {
                     h264Encoder.setWatermarkInfo(currentWatermarkInfo);
diff --git a/app/src/main/java/com/anyun/h264/H264EncodeService2.java b/app/src/main/java/com/anyun/h264/H264EncodeService2.java
new file mode 100644
index 0000000..c78a986
--- /dev/null
+++ b/app/src/main/java/com/anyun/h264/H264EncodeService2.java
@@ -0,0 +1,720 @@
+package com.anyun.h264;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import timber.log.Timber;
+
+import com.anyun.h264.model.ResourceInfo;
+import com.anyun.h264.model.WatermarkInfo;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * H264缂栫爜鏈嶅姟2锛堢浜屼釜鎽勫儚澶达紝杩愯鍦ㄧ嫭绔嬭繘绋嬶級
+ * 鎻愪緵AIDL鎺ュ彛渚涘鎴风璋冪敤锛岀敤浜庢帶鍒剁浜屼釜USB鎽勫儚澶寸殑H264缂栫爜
+ */
+public class H264EncodeService2 extends Service {
+    private static final String TAG = "H264EncodeService2";
+    
+    private H264Encoder h264Encoder;
+    private H264FileTransmitter h264FileTransmitter; // H264鏂囦欢浼犺緭鍣�
+    private String outputFileDirectory; // H264鏂囦欢杈撳嚭鐩綍
+    private WatermarkInfo currentWatermarkInfo; // 褰撳墠姘村嵃淇℃伅
+    
+    // 榛樿缂栫爜鍙傛暟
+    private static final int DEFAULT_WIDTH = 640;
+    private static final int DEFAULT_HEIGHT = 480;
+    private static final int DEFAULT_FRAME_RATE = 25;
+    private static final int DEFAULT_BITRATE = 2000000; // 2Mbps
+    
+    // 绗簩涓憚鍍忓ご鍥哄畾浣跨敤cameraId=2
+    private static final int[] CAMERA2_ID_RANGE = {2, 3};
+    
+    // AIDL鎺ュ彛瀹炵幇
+    private final IH264EncodeService.Stub binder = new IH264EncodeService.Stub() {
+        @Override
+        public int controlEncode(int action, String jsonConfig) throws RemoteException {
+            return H264EncodeService2.this.controlEncode(action, jsonConfig);
+        }
+        
+        @Override
+        public List<ResourceInfo> getResourceList(String startTime, String endTime) throws RemoteException {
+            return H264EncodeService2.this.getResourceList(startTime, endTime);
+        }
+        
+        @Override
+        public void setWatermarkInfo(String watermarkInfo) throws RemoteException {
+            H264EncodeService2.this.setWatermarkInfo(watermarkInfo);
+        }
+    };
+    
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Timber.d("H264EncodeService2 created (process 2 for camera 2)");
+        
+        // 鍒濆鍖栬緭鍑烘枃浠剁洰褰曪紙浣跨敤搴旂敤澶栭儴瀛樺偍鐩綍锛�
+        outputFileDirectory = getExternalFilesDir(null).getAbsolutePath();
+        Timber.d("Output file directory: %s", outputFileDirectory);
+    }
+    
+    @Override
+    public IBinder onBind(Intent intent) {
+        Timber.d("Service2 bound");
+        return binder;
+    }
+    
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Timber.d("Service2 unbound");
+        // 涓嶈嚜鍔ㄥ仠姝㈢紪鐮佸櫒锛岃瀹冨湪鏈嶅姟涓繚鎸佽繍琛�
+        return super.onUnbind(intent);
+    }
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Timber.d("Service2 destroyed");
+        
+        // 鍋滄骞堕噴鏀剧紪鐮佸櫒鍜屾枃浠朵紶杈撳櫒
+        stopEncoder();
+        stopFileTransmitter();
+    }
+    
+    /**
+     * 缂栫爜閰嶇疆绫�
+     */
+    private static class EncodeConfig {
+        String ip;
+        int port;
+        int width;
+        int height;
+        int framerate;
+        String simPhone;
+        
+        // 浠嶫SON瑙f瀽閰嶇疆
+        static EncodeConfig fromJson(String jsonConfig) throws JSONException {
+            EncodeConfig config = new EncodeConfig();
+            if (jsonConfig == null || jsonConfig.trim().isEmpty()) {
+                // 浣跨敤榛樿鍊�
+                config.width = DEFAULT_WIDTH;
+                config.height = DEFAULT_HEIGHT;
+                config.framerate = DEFAULT_FRAME_RATE;
+                config.ip = null;
+                config.port = 0;
+                config.simPhone = null;
+                return config;
+            }
+            
+            JSONObject json = new JSONObject(jsonConfig);
+            config.width = json.optInt("width", DEFAULT_WIDTH);
+            config.height = json.optInt("height", DEFAULT_HEIGHT);
+            config.framerate = json.optInt("framerate", DEFAULT_FRAME_RATE);
+            config.ip = json.optString("ip", null);
+            config.port = json.optInt("port", 0);
+            config.simPhone = json.optString("simPhone", null);
+            
+            return config;
+        }
+    }
+    
+    /**
+     * 鏂囦欢浼犺緭閰嶇疆绫�
+     */
+    private static class FileTransmitConfig {
+        String ip;
+        int port;
+        int framerate;
+        String simPhone;
+        String filePath; // H264鏂囦欢璺緞
+        int protocolType; // 鍗忚绫诲瀷锛�1-UDP锛�2-TCP
+        
+        // 浠嶫SON瑙f瀽閰嶇疆
+        static FileTransmitConfig fromJson(String jsonConfig) throws JSONException {
+            FileTransmitConfig config = new FileTransmitConfig();
+            if (jsonConfig == null || jsonConfig.trim().isEmpty()) {
+                throw new JSONException("File transmit config cannot be empty");
+            }
+            
+            JSONObject json = new JSONObject(jsonConfig);
+            config.ip = json.optString("ip", null);
+            config.port = json.optInt("port", 0);
+            config.framerate = json.optInt("framerate", DEFAULT_FRAME_RATE);
+            config.simPhone = json.optString("simPhone", "013120122580");
+            config.filePath = json.optString("filePath", null);
+            // 鍗忚绫诲瀷锛氶粯璁CP锛�2锛夛紝1-UDP锛�2-TCP
+            config.protocolType = json.optInt("protocolType", JT1076ProtocolHelper.PROTOCOL_TYPE_TCP);
+            
+            return config;
+        }
+    }
+    
+    /**
+     * 鎺у埗H264缂栫爜鍜屾枃浠朵紶杈擄紙绗簩涓憚鍍忓ご锛�
+     */
+    private synchronized int controlEncode(int action, String jsonConfig) {
+        Timber.d("controlEncode (camera2) called with action: %d, jsonConfig: %s", action, jsonConfig);
+        
+        try {
+            switch (action) {
+                case 0: // 寮�鍚痟264鏂囦欢鍐欏叆
+                    try {
+                        EncodeConfig config0 = EncodeConfig.fromJson(jsonConfig);
+                        return startFileEncode(config0);
+                    } catch (JSONException e) {
+                        Timber.e(e, "Failed to parse JSON config: %s", jsonConfig);
+                        return 1;
+                    }
+                    
+                case 1: // 鍋滄h264缂栫爜骞跺仠姝㈠啓鍏ユ枃浠�
+                    return stopEncoder();
+                    
+                case 2: // 寮�鍚綉缁滄帹閫乭264锛堜笉鍐欏叆鏂囦欢锛�
+                    try {
+                        EncodeConfig config2 = EncodeConfig.fromJson(jsonConfig);
+                        return startNetworkEncode(config2);
+                    } catch (JSONException e) {
+                        Timber.e(e, "Failed to parse JSON config: %s", jsonConfig);
+                        return 1;
+                    }
+                    
+                case 3: // 鍋滄h264缂栫爜骞跺仠姝㈢綉缁滄帹閫�
+                    return stopEncoder();
+                    
+                case 4: // 寮�濮嬩紶杈揌264鏂囦欢
+                    try {
+                        FileTransmitConfig config4 = FileTransmitConfig.fromJson(jsonConfig);
+                        return startFileTransmit(config4);
+                    } catch (JSONException e) {
+                        Timber.e(e, "Failed to parse JSON config: %s", jsonConfig);
+                        return 1;
+                    }
+                    
+                case 5: // 鍋滄H264鏂囦欢浼犺緭
+                    Timber.i("瀹㈡埛绔姹傚仠姝㈣棰戞枃浠朵笂浼� (camera2)");
+                    return stopFileTransmitter();
+                    
+                default:
+                    Timber.e("Unknown action: %d", action);
+                    return 1; // 澶辫触
+            }
+        } catch (Exception e) {
+            Timber.e(e, "Error in controlEncode (camera2)");
+            return 1; // 澶辫触
+        }
+    }
+    
+    /**
+     * 鍚姩鏂囦欢缂栫爜妯″紡锛堝彧鍐欏叆鏂囦欢锛屼笉杩涜缃戠粶鎺ㄩ�侊級
+     */
+    private int startFileEncode(EncodeConfig config) {
+        Timber.d("Starting file encode mode (camera2)");
+        
+        // 濡傛灉缂栫爜鍣ㄥ凡缁忓湪杩愯锛屽厛鍋滄
+        if (h264Encoder != null) {
+            Timber.w("Encoder is already running (camera2), stopping it first");
+            stopEncoder();
+        }
+        
+        try {
+            // 鍒涘缓缂栫爜鍣�
+            h264Encoder = new H264Encoder();
+            
+            // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
+            // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
+            int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH;
+            int height = config != null && config.height > 0 ? config.height : DEFAULT_HEIGHT;
+            int framerate = config != null && config.framerate > 0 ? config.framerate : DEFAULT_FRAME_RATE;
+            h264Encoder.setEncoderParams(width, height, framerate, DEFAULT_BITRATE);
+
+            long timeFile = System.currentTimeMillis()/1000*1000;
+            SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss");
+            String str = bcdFormat.format(timeFile);
+            Timber.i("鏂囦欢鍚� (camera2)锛�%s", str);
+            // 璁剧疆杈撳嚭鏂囦欢锛堟坊鍔燾amera2鏍囪瘑锛�
+            String fileName = "h264_camera2_" + timeFile+ ".h264";
+            File outputFile = new File(outputFileDirectory, fileName);
+            h264Encoder.setOutputFile(outputFile.getAbsolutePath());
+            h264Encoder.setEnableFileOutput(true); // 鍚敤鏂囦欢杈撳嚭
+            
+            // 绂佺敤缃戠粶浼犺緭
+            h264Encoder.setEnableNetworkTransmission(false);
+            
+            // 鍒濆鍖栧苟鍚姩锛堜娇鐢ㄧ浜屼釜鎽勫儚澶达紝cameraId=2锛�
+            int[] resolution = {width, height};
+            if (h264Encoder.initialize(CAMERA2_ID_RANGE, null, resolution, false)) {
+                // 搴旂敤宸蹭繚瀛樼殑姘村嵃淇℃伅锛堝鏋滄湁锛�
+                if (currentWatermarkInfo != null) {
+                    h264Encoder.setWatermarkInfo(currentWatermarkInfo);
+                    Timber.d("Applied saved watermark info to encoder (camera2)");
+                }
+                h264Encoder.start();
+                Timber.d("File encode started successfully (camera2), output file: %s, resolution: %dx%d, framerate: %d", 
+                        outputFile.getAbsolutePath(), width, height, framerate);
+                return 0; // 鎴愬姛
+            } else {
+                Timber.e("Failed to initialize encoder (camera2)");
+                h264Encoder = null;
+                return 1; // 澶辫触
+            }
+        } catch (Exception e) {
+            Timber.e(e, "Failed to start file encode (camera2)");
+            h264Encoder = null;
+            return 1; // 澶辫触
+        }
+    }
+    
+    /**
+     * 鍚姩缃戠粶鎺ㄩ�佹ā寮忥紙鍙繘琛岀綉缁滄帹閫侊紝涓嶅啓鍏ユ枃浠讹級
+     */
+    private int startNetworkEncode(EncodeConfig config) {
+        Timber.d("Starting network encode mode (camera2)");
+        
+        // 濡傛灉缂栫爜鍣ㄥ凡缁忓湪杩愯锛屽厛鍋滄
+        if (h264Encoder != null) {
+            Timber.w("Encoder is already running (camera2), stopping it first");
+            stopEncoder();
+        }
+        
+        // 妫�鏌ュ繀闇�鐨勯厤缃弬鏁�
+        if (config == null || config.ip == null || config.ip.trim().isEmpty() || config.port <= 0) {
+            Timber.e("Network encode requires valid ip and port in config (camera2)");
+            return 1; // 澶辫触
+        }
+        
+        try {
+            // 鍒涘缓缂栫爜鍣�
+            h264Encoder = new H264Encoder();
+            
+            // 璁剧疆缂栫爜鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨勫弬鏁帮級
+            int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH;
+            int height = config != null && config.height > 0 ? config.height : DEFAULT_HEIGHT;
+            int framerate = config != null && config.framerate > 0 ? config.framerate : DEFAULT_FRAME_RATE;
+            h264Encoder.setEncoderParams(width, height, framerate, DEFAULT_BITRATE);
+
+            long timeFile = System.currentTimeMillis()/1000*1000;
+            SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss");
+            String str = bcdFormat.format(timeFile);
+            Timber.i("startNetworkEncode (camera2) 鏂囦欢鍚嶏細%s", str);
+            // 璁剧疆杈撳嚭鏂囦欢锛堟坊鍔燾amera2鏍囪瘑锛�
+            String fileName = "h264_camera2_" + timeFile+ ".h264";
+            File outputFile = new File(outputFileDirectory, fileName);
+            h264Encoder.setOutputFile(outputFile.getAbsolutePath());
+            h264Encoder.setEnableFileOutput(true); // 鍚敤鏂囦欢杈撳嚭
+
+            
+            // 鍚敤缃戠粶浼犺緭骞惰缃湇鍔″櫒鍦板潃
+            h264Encoder.setEnableNetworkTransmission(true);
+            h264Encoder.setServerAddress(config.ip, config.port);
+            
+            // 璁剧疆鍗忚鍙傛暟锛堜娇鐢ㄩ厤缃腑鐨剆imPhone锛屽鏋滄湭鎻愪緵鍒欎娇鐢ㄩ粯璁ゅ�硷級
+            String simPhone = config.simPhone != null && !config.simPhone.trim().isEmpty() 
+                    ? config.simPhone : "013120122580";
+            h264Encoder.setProtocolParams(simPhone, (byte)2); // 绗簩涓憚鍍忓ご浣跨敤channelId=2
+            
+            // 鍒濆鍖栧苟鍚姩锛堜娇鐢ㄧ浜屼釜鎽勫儚澶达紝cameraId=2锛�
+            int[] resolution = {width, height};
+            if (h264Encoder.initialize(CAMERA2_ID_RANGE, null, resolution, false)) {
+                // 搴旂敤宸蹭繚瀛樼殑姘村嵃淇℃伅锛堝鏋滄湁锛�
+                if (currentWatermarkInfo != null) {
+                    h264Encoder.setWatermarkInfo(currentWatermarkInfo);
+                    Timber.d("Applied saved watermark info to encoder (camera2)");
+                }
+                h264Encoder.start();
+                Timber.d("Network encode started successfully (camera2), server: %s:%d, resolution: %dx%d, framerate: %d", 
+                        config.ip, config.port, width, height, framerate);
+                return 0; // 鎴愬姛
+            } else {
+                Timber.e("Failed to initialize encoder (camera2)");
+                h264Encoder = null;
+                return 1; // 澶辫触
+            }
+        } catch (Exception e) {
+            Timber.e(e, "Failed to start network encode (camera2)");
+            h264Encoder = null;
+            return 1; // 澶辫触
+        }
+    }
+    
+    /**
+     * 鍋滄缂栫爜鍣�
+     */
+    private int stopEncoder() {
+        Timber.d("Stopping encoder (camera2)");
+        
+        if (h264Encoder != null) {
+            try {
+                h264Encoder.stop();
+                h264Encoder.release();
+                h264Encoder = null;
+                Timber.d("Encoder stopped successfully (camera2)");
+                return 0; // 鎴愬姛
+            } catch (Exception e) {
+                Timber.e(e, "Error stopping encoder (camera2)");
+                h264Encoder = null;
+                return 1; // 澶辫触
+            }
+        } else {
+            Timber.w("Encoder is not running (camera2)");
+            return 0; // 鎴愬姛锛堟病鏈夎繍琛岀殑缂栫爜鍣紝瑙嗕负鎴愬姛锛�
+        }
+    }
+    
+    /**
+     * 鍚姩鏂囦欢浼犺緭妯″紡锛堜粠H264鏂囦欢璇诲彇骞剁綉缁滄帹閫侊級
+     */
+    private int startFileTransmit(FileTransmitConfig config) {
+        Timber.d("Starting file transmit mode (camera2)");
+        
+        // 濡傛灉鏂囦欢浼犺緭鍣ㄥ凡缁忓湪杩愯锛屽厛鍋滄
+        if (h264FileTransmitter != null) {
+            Timber.w("File transmitter is already running (camera2), stopping it first");
+            stopFileTransmitter();
+        }
+        
+        // 妫�鏌ュ繀闇�鐨勯厤缃弬鏁�
+        if (config == null || config.ip == null || config.ip.trim().isEmpty() || config.port <= 0) {
+            Timber.e("File transmit requires valid ip and port in config (camera2)");
+            return 1; // 澶辫触
+        }
+        
+        if (config.filePath == null || config.filePath.trim().isEmpty()) {
+            Timber.e("File transmit requires valid filePath in config (camera2)");
+            return 1; // 澶辫触
+        }
+        
+        try {
+            // 妫�鏌ユ枃浠舵槸鍚﹀瓨鍦�
+            File file = new File(config.filePath);
+            if (!file.exists() || !file.isFile()) {
+                Timber.e("File does not exist: %s (camera2)", config.filePath);
+                return 1; // 澶辫触
+            }
+            
+            // 鍒涘缓鏂囦欢浼犺緭鍣�
+            h264FileTransmitter = new H264FileTransmitter();
+            
+            // 璁剧疆鏈嶅姟鍣ㄥ湴鍧�
+            h264FileTransmitter.setServerAddress(config.ip, config.port);
+            
+            // 璁剧疆鍗忚绫诲瀷
+            h264FileTransmitter.setProtocolType(1); //1-tcp
+            
+            // 璁剧疆鍗忚鍙傛暟锛圫IM鍗″彿鍜岄�昏緫閫氶亾鍙凤紝绗簩涓憚鍍忓ご浣跨敤channelId=2锛�
+            String simPhone = config.simPhone != null && !config.simPhone.trim().isEmpty() 
+                    ? config.simPhone : "013120122580";
+            h264FileTransmitter.setProtocolParams(simPhone, (byte)2);
+            
+            // 璁剧疆甯х巼锛堢敤浜庤绠楁椂闂存埑闂撮殧锛�
+            int framerate = config.framerate > 0 ? config.framerate : DEFAULT_FRAME_RATE;
+            h264FileTransmitter.setFrameRate(framerate);
+            
+            // 璁剧疆杩涘害鍥炶皟锛堝彲閫夛紝鐢ㄤ簬鏃ュ織杈撳嚭锛�
+            h264FileTransmitter.setOnTransmitProgressCallback(new H264FileTransmitter.OnTransmitProgressCallback() {
+                @Override
+                public void onProgress(int currentFrame, int totalFrames) {
+                    Timber.d("File transmit progress (camera2): frame %d%s", currentFrame, 
+                            totalFrames > 0 ? " of " + totalFrames : "");
+                }
+                
+                @Override
+                public void onComplete() {
+                    Timber.d("File transmit completed (camera2)");
+                    stopFileTransmitter();
+                }
+                
+                @Override
+                public void onError(String error) {
+                    Timber.e("File transmit error (camera2): %s", error);
+                }
+            });
+            
+            // 鍒濆鍖朣ocket杩炴帴
+            if (!h264FileTransmitter.initialize()) {
+                Timber.e("Failed to initialize file transmitter socket (camera2)");
+                h264FileTransmitter = null;
+                return 1; // 澶辫触
+            }
+            
+            // 寮�濮嬩紶杈撴枃浠�
+            h264FileTransmitter.transmitFile(config.filePath);
+            
+            Timber.d("File transmit started successfully (camera2), file: %s, server: %s:%d, protocol: %s, framerate: %d", 
+                    config.filePath, config.ip, config.port, 
+                    config.protocolType == JT1076ProtocolHelper.PROTOCOL_TYPE_UDP ? "UDP" : "TCP", framerate);
+            return 0; // 鎴愬姛
+            
+        } catch (Exception e) {
+            Timber.e(e, "Failed to start file transmit (camera2)");
+            if (h264FileTransmitter != null) {
+                try {
+                    h264FileTransmitter.stop();
+                } catch (Exception ex) {
+                    Timber.e(ex, "Error stopping file transmitter after failure (camera2)");
+                }
+                h264FileTransmitter = null;
+            }
+            return 1; // 澶辫触
+        }
+    }
+    
+    /**
+     * 鍋滄鏂囦欢浼犺緭鍣�
+     */
+    private int stopFileTransmitter() {
+        Timber.d("Stopping file transmitter (camera2)");
+        
+        if (h264FileTransmitter != null) {
+            try {
+                h264FileTransmitter.stop();
+                h264FileTransmitter = null;
+                Timber.d("File transmitter stopped successfully (camera2)");
+                return 0; // 鎴愬姛
+            } catch (Exception e) {
+                Timber.e(e, "Error stopping file transmitter (camera2)");
+                h264FileTransmitter = null;
+                return 1; // 澶辫触
+            }
+        } else {
+            Timber.w("File transmitter is not running (camera2)");
+            return 0; // 鎴愬姛锛堟病鏈夎繍琛岀殑鏂囦欢浼犺緭鍣紝瑙嗕负鎴愬姛锛�
+        }
+    }
+    
+    /**
+     * 鑾峰彇璧勬簮鍒楄〃锛堟牴鎹甁T/T 1076-2016琛�23瀹氫箟锛�
+     */
+    private List<ResourceInfo> getResourceList(String startTime, String endTime) {
+        Timber.d("getResourceList called (camera2), startTime: %s, endTime: %s", startTime, endTime);
+        
+        List<ResourceInfo> resourceList = new ArrayList<>();
+        
+        try {
+            // 鎵弿杈撳嚭鐩綍涓殑H264鏂囦欢锛堝彧鏌ユ壘camera2鐨勬枃浠讹級
+            File dir = new File(outputFileDirectory);
+            if (!dir.exists() || !dir.isDirectory()) {
+                Timber.w("Output directory does not exist: %s", outputFileDirectory);
+                return resourceList;
+            }
+            
+            File[] files = dir.listFiles((dir1, name) -> 
+                name.toLowerCase().endsWith(".h264") && name.contains("camera2"));
+            if (files == null || files.length == 0) {
+                Timber.d("No H264 files found for camera2 in directory");
+                return resourceList;
+            }
+            
+            // 瑙f瀽鏃堕棿鑼冨洿
+            Date startDate = parseTime(startTime);
+            Date endDate = parseTime(endTime);
+            
+            if (startDate == null || endDate == null) {
+                Timber.e("Invalid time format, startTime: %s, endTime: %s", startTime, endTime);
+                return resourceList;
+            }
+            
+            // 閬嶅巻鏂囦欢锛屾煡鎵惧湪鏃堕棿鑼冨洿鍐呯殑鏂囦欢
+            for (File file : files) {
+                ResourceInfo resourceInfo = createResourceInfoFromFile(file, startDate, endDate);
+                if (resourceInfo != null) {
+                    resourceList.add(resourceInfo);
+                }
+            }
+            
+            Timber.d("Found %d resources for camera2 in time range", resourceList.size());
+            return resourceList;
+            
+        } catch (Exception e) {
+            Timber.e(e, "Error getting resource list (camera2)");
+            return resourceList;
+        }
+    }
+    
+    /**
+     * 璁剧疆姘村嵃淇℃伅
+     */
+    private void setWatermarkInfo(String watermarkInfoJson) {
+        Timber.d("setWatermarkInfo called (camera2), watermarkInfoJson: %s", watermarkInfoJson);
+        
+        try {
+            if (watermarkInfoJson == null || watermarkInfoJson.trim().isEmpty()) {
+                Timber.w("Watermark info JSON is null or empty, clearing watermark (camera2)");
+                currentWatermarkInfo = null;
+                // 濡傛灉缂栫爜鍣ㄦ鍦ㄨ繍琛岋紝娓呴櫎姘村嵃
+                if (h264Encoder != null) {
+                    h264Encoder.setWatermarkInfo(null);
+                }
+                return;
+            }
+            
+            // 瑙f瀽JSON
+            JSONObject json = new JSONObject(watermarkInfoJson);
+            WatermarkInfo watermarkInfo = new WatermarkInfo();
+            
+            // 瑙f瀽鍚勪釜瀛楁锛堜娇鐢� optString/optDouble 閬垮厤瀛楁涓嶅瓨鍦ㄦ椂鎶涘嚭寮傚父锛�
+            watermarkInfo.setPlateNumber(json.optString("plateNumber", null));
+            watermarkInfo.setStudent(json.optString("student", null));
+            watermarkInfo.setCoach(json.optString("coach", null));
+            
+            // 缁忓害鍜岀含搴﹀彲鑳芥槸鏁板瓧鎴栧瓧绗︿覆
+            if (json.has("longitude")) {
+                Object lonObj = json.get("longitude");
+                if (lonObj instanceof Number) {
+                    watermarkInfo.setLongitude(((Number) lonObj).doubleValue());
+                } else if (lonObj instanceof String) {
+                    try {
+                        watermarkInfo.setLongitude(Double.parseDouble((String) lonObj));
+                    } catch (NumberFormatException e) {
+                        Timber.w("Invalid longitude format: %s", lonObj);
+                    }
+                }
+            }
+            
+            if (json.has("latitude")) {
+                Object latObj = json.get("latitude");
+                if (latObj instanceof Number) {
+                    watermarkInfo.setLatitude(((Number) latObj).doubleValue());
+                } else if (latObj instanceof String) {
+                    try {
+                        watermarkInfo.setLatitude(Double.parseDouble((String) latObj));
+                    } catch (NumberFormatException e) {
+                        Timber.w("Invalid latitude format: %s", latObj);
+                    }
+                }
+            }
+            
+            watermarkInfo.setDrivingSchool(json.optString("drivingSchool", null));
+            
+            // 杞﹂�熷彲鑳芥槸鏁板瓧鎴栧瓧绗︿覆
+            if (json.has("speed")) {
+                Object speedObj = json.get("speed");
+                if (speedObj instanceof Number) {
+                    watermarkInfo.setSpeed(((Number) speedObj).doubleValue());
+                } else if (speedObj instanceof String) {
+                    try {
+                        watermarkInfo.setSpeed(Double.parseDouble((String) speedObj));
+                    } catch (NumberFormatException e) {
+                        Timber.w("Invalid speed format: %s", speedObj);
+                    }
+                }
+            }
+            
+            // 淇濆瓨姘村嵃淇℃伅
+            currentWatermarkInfo = watermarkInfo;
+            Timber.i("Watermark info parsed successfully (camera2): %s", watermarkInfo);
+            
+            // 濡傛灉缂栫爜鍣ㄦ鍦ㄨ繍琛岋紝绔嬪嵆搴旂敤姘村嵃
+            if (h264Encoder != null) {
+                h264Encoder.setWatermarkInfo(watermarkInfo);
+                Timber.d("Watermark applied to encoder (camera2)");
+            } else {
+                Timber.d("Encoder not running, watermark will be applied when encoder starts (camera2)");
+            }
+            
+        } catch (JSONException e) {
+            Timber.e(e, "Failed to parse watermark info JSON (camera2): %s", watermarkInfoJson);
+            currentWatermarkInfo = null;
+        } catch (Exception e) {
+            Timber.e(e, "Unexpected error setting watermark info (camera2)");
+            currentWatermarkInfo = null;
+        }
+    }
+    
+    /**
+     * 浠庢枃浠跺垱寤鸿祫婧愪俊鎭紙濡傛灉鏂囦欢鍦ㄦ椂闂磋寖鍥村唴锛�
+     */
+    private ResourceInfo createResourceInfoFromFile(File file, Date startDate, Date endDate) {
+        try {
+            // 浠庢枃浠跺悕涓彁鍙栨椂闂存埑锛堟牸寮忥細h264_camera2_1234567890123.h264锛�
+            String fileName = file.getName();
+            Date startTimeFromFileName = null;
+            
+            if (fileName.startsWith("h264_camera2_") && fileName.endsWith(".h264")) {
+                try {
+                    // 鎻愬彇鏂囦欢鍚嶄腑鐨勬椂闂存埑
+                    String timestampStr = fileName.substring(14, fileName.length() - 5); // 鍘绘帀 "h264_camera2_" 鍜� ".h264"
+                    long timestamp = Long.parseLong(timestampStr);
+                    startTimeFromFileName = new Date(timestamp);
+                } catch (NumberFormatException e) {
+                    Timber.w("Failed to parse timestamp from filename: %s", fileName);
+                }
+            }
+            
+            // 濡傛灉鏃犳硶浠庢枃浠跺悕瑙f瀽鏃堕棿鎴筹紝鍒欎娇鐢ㄦ枃浠朵慨鏀规椂闂翠綔涓哄紑濮嬫椂闂�
+            if (startTimeFromFileName == null) {
+                startTimeFromFileName = new Date(file.lastModified());
+            }
+            
+            // 缁撴潫鏃堕棿浣跨敤鏂囦欢淇敼鏃堕棿
+            Date endTimeFromFile = new Date(file.lastModified());
+            
+            // 妫�鏌ユ枃浠舵椂闂存槸鍚﹀湪鎸囧畾鑼冨洿鍐�
+            if (startTimeFromFileName.after(endDate) || endTimeFromFile.before(startDate)) {
+                return null; // 涓嶅湪鏃堕棿鑼冨洿鍐�
+            }
+            
+            // 鍒涘缓璧勬簮淇℃伅瀵硅薄
+            ResourceInfo resourceInfo = new ResourceInfo();
+            
+            // 閫昏緫閫氶亾鍙凤紙绗簩涓憚鍍忓ご浣跨敤channelId=2锛�
+            resourceInfo.setLogicalChannelNumber((byte) 2);
+            
+            // 寮�濮嬫椂闂达細浠庢枃浠跺悕涓殑鏃堕棿鎴�
+            SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss", Locale.CHINA);
+            resourceInfo.setStartTime(bcdFormat.format(startTimeFromFileName));
+            
+            // 缁撴潫鏃堕棿锛氫娇鐢ㄦ枃浠朵慨鏀规椂闂�
+            resourceInfo.setEndTime(bcdFormat.format(endTimeFromFile));
+            
+            // 鎶ヨ鏍囧織锛堥粯璁ゅ�硷紝瀹為檯搴斾粠鏂囦欢鍏冩暟鎹幏鍙栵級
+            resourceInfo.setAlarmFlag(0L);
+            
+            // 闊宠棰戣祫婧愮被鍨嬶細2-瑙嗛
+            resourceInfo.setResourceType((byte) 2);
+            
+            // 鐮佹祦绫诲瀷锛�1-涓荤爜娴�
+            resourceInfo.setStreamType((byte) 1);
+            
+            // 瀛樺偍鍣ㄧ被鍨嬶細1-涓诲瓨鍌ㄥ櫒
+            resourceInfo.setStorageType((byte) 1);
+            
+            // 鏂囦欢澶у皬
+            resourceInfo.setFileSize(file.length());
+            
+            return resourceInfo;
+            
+        } catch (Exception e) {
+            Timber.e(e, "Error creating resource info from file: %s", file.getName());
+            return null;
+        }
+    }
+    
+    /**
+     * 瑙f瀽BCD鏃堕棿瀛楃涓�
+     */
+    private Date parseTime(String timeStr) {
+        if (timeStr == null || timeStr.length() != 12) {
+            return null;
+        }
+        
+        try {
+            SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss", Locale.CHINA);
+            return format.parse(timeStr);
+        } catch (ParseException e) {
+            Timber.e(e, "Failed to parse time: %s", timeStr);
+            return null;
+        }
+    }
+}
+

--
Gitblit v1.8.0