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