| app/build.gradle | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/AndroidManifest.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/FileLoggingTree.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/H264Application.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/H264EncodeService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/H264FileTransmitter.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/MainActivity.kt | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| gradle/libs.versions.toml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
app/build.gradle
@@ -68,6 +68,7 @@ implementation libs.androidx.ui.tooling.preview implementation libs.androidx.material3 implementation libs.netty.all implementation libs.timber testImplementation libs.junit androidTestImplementation libs.androidx.junit androidTestImplementation libs.androidx.espresso.core app/src/main/AndroidManifest.xml
@@ -7,10 +7,15 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 存储权限(用于日志文件) --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- USB摄像头权限 --> <uses-feature android:name="android.hardware.usb.host" /> <application android:name=".H264Application" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" app/src/main/java/com/anyun/h264/FileLoggingTree.java
New file @@ -0,0 +1,146 @@ package com.anyun.h264; import android.os.Environment; import android.util.Log; import timber.log.Timber; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * Timber Tree implementation that logs to files. * Logs are saved to sdcard/nvlog/h264_yyyyMMdd.log format. */ public class FileLoggingTree extends Timber.DebugTree { private static final String LOG_DIR = "nvlog"; private static final String LOG_PREFIX = "h264_"; private static final String LOG_SUFFIX = ".log"; private static final String DATE_FORMAT = "yyyyMMdd"; private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; private String currentLogFile = null; private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.CHINA); private SimpleDateFormat timestampFormat = new SimpleDateFormat(TIMESTAMP_FORMAT, Locale.CHINA); @Override protected void log(int priority, String tag, String message, Throwable t) { try { // Get today's log file path String today = dateFormat.format(new Date()); String logFileName = LOG_PREFIX + today + LOG_SUFFIX; // Check if we need to update the log file (new day) if (!logFileName.equals(currentLogFile)) { currentLogFile = logFileName; } File logFile = getLogFile(logFileName); if (logFile == null) { return; } // Format log entry String logEntry = formatLogEntry(priority, tag, message, t); // Write to file (append mode) synchronized (this) { try (FileWriter writer = new FileWriter(logFile, true)) { writer.append(logEntry); writer.append("\n"); writer.flush(); } catch (IOException e) { // If file writing fails, log to system log as fallback Log.e("FileLoggingTree", "Failed to write log to file", e); } } } catch (Exception e) { // If anything goes wrong, log to system log Log.e("FileLoggingTree", "Error in FileLoggingTree", e); } } /** * Get the log file. Creates directory if needed. */ private File getLogFile(String fileName) { try { File logDir = new File(Environment.getExternalStorageDirectory(), LOG_DIR); if (!logDir.exists()) { if (!logDir.mkdirs()) { Log.e("FileLoggingTree", "Failed to create log directory: " + logDir.getAbsolutePath()); return null; } } File logFile = new File(logDir, fileName); if (!logFile.exists()) { if (!logFile.createNewFile()) { Log.e("FileLoggingTree", "Failed to create log file: " + logFile.getAbsolutePath()); return null; } } return logFile; } catch (IOException e) { Log.e("FileLoggingTree", "Error getting log file", e); return null; } } /** * Format a log entry with timestamp, priority, tag, and message. */ private String formatLogEntry(int priority, String tag, String message, Throwable t) { StringBuilder sb = new StringBuilder(); // Timestamp sb.append(timestampFormat.format(new Date())); sb.append(" "); // Priority level sb.append(getPriorityString(priority)); sb.append("/"); // Tag sb.append(tag); sb.append(": "); // Message sb.append(message); // Throwable stack trace if (t != null) { sb.append("\n"); sb.append(Log.getStackTraceString(t)); } return sb.toString(); } /** * Get priority string representation. */ private String getPriorityString(int priority) { switch (priority) { case Log.VERBOSE: return "V"; case Log.DEBUG: return "D"; case Log.INFO: return "I"; case Log.WARN: return "W"; case Log.ERROR: return "E"; case Log.ASSERT: return "A"; default: return "?"; } } } app/src/main/java/com/anyun/h264/H264Application.java
New file @@ -0,0 +1,27 @@ package com.anyun.h264; import android.app.Application; import timber.log.Timber; /** * Application class for initializing Timber logging. */ public class H264Application extends Application { @Override public void onCreate() { super.onCreate(); // Initialize Timber // if (BuildConfig.DEBUG) { // In debug mode, use both console and file logging Timber.plant(new Timber.DebugTree()); Timber.plant(new FileLoggingTree()); // } else { // // In release mode, only use file logging // Timber.plant(new FileLoggingTree()); // } Timber.d("H264Application onCreate - Timber initialized"); } } app/src/main/java/com/anyun/h264/H264EncodeService.java
@@ -4,7 +4,7 @@ import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import timber.log.Timber; import com.anyun.h264.model.ResourceInfo; @@ -56,22 +56,22 @@ @Override public void onCreate() { super.onCreate(); Log.d(TAG, "H264EncodeService created"); Timber.d("H264EncodeService created"); // 初始化输出文件目录(使用应用外部存储目录) outputFileDirectory = getExternalFilesDir(null).getAbsolutePath(); Log.d(TAG, "Output file directory: " + outputFileDirectory); Timber.d("Output file directory: %s", outputFileDirectory); } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "Service bound"); Timber.d("Service bound"); return binder; } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "Service unbound"); Timber.d("Service unbound"); // 不自动停止编码器,让它在服务中保持运行 return super.onUnbind(intent); } @@ -79,7 +79,7 @@ @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "Service destroyed"); Timber.d("Service destroyed"); // 停止并释放编码器和文件传输器 stopEncoder(); @@ -170,7 +170,7 @@ * @return 0-成功,1-失败 */ private synchronized int controlEncode(int action, String jsonConfig) { Log.d(TAG, "controlEncode called with action: " + action + ", jsonConfig: " + jsonConfig); Timber.d("controlEncode called with action: %d, jsonConfig: %s", action, jsonConfig); try { switch (action) { @@ -179,7 +179,7 @@ EncodeConfig config0 = EncodeConfig.fromJson(jsonConfig); return startFileEncode(config0); } catch (JSONException e) { Log.e(TAG, "Failed to parse JSON config: " + jsonConfig, e); Timber.e(e, "Failed to parse JSON config: %s", jsonConfig); return 1; } @@ -191,7 +191,7 @@ EncodeConfig config2 = EncodeConfig.fromJson(jsonConfig); return startNetworkEncode(config2); } catch (JSONException e) { Log.e(TAG, "Failed to parse JSON config: " + jsonConfig, e); Timber.e(e, "Failed to parse JSON config: %s", jsonConfig); return 1; } @@ -203,7 +203,7 @@ FileTransmitConfig config4 = FileTransmitConfig.fromJson(jsonConfig); return startFileTransmit(config4); } catch (JSONException e) { Log.e(TAG, "Failed to parse JSON config: " + jsonConfig, e); Timber.e(e, "Failed to parse JSON config: %s", jsonConfig); return 1; } @@ -211,11 +211,11 @@ return stopFileTransmitter(); default: Log.e(TAG, "Unknown action: " + action); Timber.e("Unknown action: %d", action); return 1; // 失败 } } catch (Exception e) { Log.e(TAG, "Error in controlEncode", e); Timber.e(e, "Error in controlEncode"); return 1; // 失败 } } @@ -224,11 +224,11 @@ * 启动文件编码模式(只写入文件,不进行网络推送) */ private int startFileEncode(EncodeConfig config) { Log.d(TAG, "Starting file encode mode"); Timber.d("Starting file encode mode"); // 如果编码器已经在运行,先停止 if (h264Encoder != null) { Log.w(TAG, "Encoder is already running, stopping it first"); Timber.w("Encoder is already running, stopping it first"); stopEncoder(); } @@ -245,7 +245,7 @@ long timeFile = System.currentTimeMillis(); SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss"); String str = bcdFormat.format(timeFile); Log.i(TAG,"文件名:"+str); Timber.i("文件名:%s", str); // 设置输出文件 String fileName = "h264_" + timeFile+ ".h264"; File outputFile = new File(outputFileDirectory, fileName); @@ -259,16 +259,16 @@ int[] resolution = {width, height}; if (h264Encoder.initialize(DEFAULT_CAMERA_ID_RANGE, null, resolution, false)) { h264Encoder.start(); Log.d(TAG, "File encode started successfully, output file: " + outputFile.getAbsolutePath() + ", resolution: " + width + "x" + height + ", framerate: " + framerate); Timber.d("File encode started successfully, output file: %s, resolution: %dx%d, framerate: %d", outputFile.getAbsolutePath(), width, height, framerate); return 0; // 成功 } else { Log.e(TAG, "Failed to initialize encoder"); Timber.e("Failed to initialize encoder"); h264Encoder = null; return 1; // 失败 } } catch (Exception e) { Log.e(TAG, "Failed to start file encode", e); Timber.e(e, "Failed to start file encode"); h264Encoder = null; return 1; // 失败 } @@ -278,17 +278,17 @@ * 启动网络推送模式(只进行网络推送,不写入文件) */ private int startNetworkEncode(EncodeConfig config) { Log.d(TAG, "Starting network encode mode"); Timber.d("Starting network encode mode"); // 如果编码器已经在运行,先停止 if (h264Encoder != null) { Log.w(TAG, "Encoder is already running, stopping it first"); Timber.w("Encoder is already running, stopping it first"); stopEncoder(); } // 检查必需的配置参数 if (config == null || config.ip == null || config.ip.trim().isEmpty() || config.port <= 0) { Log.e(TAG, "Network encode requires valid ip and port in config"); Timber.e("Network encode requires valid ip and port in config"); return 1; // 失败 } @@ -321,16 +321,16 @@ int[] resolution = {width, height}; if (h264Encoder.initialize(DEFAULT_CAMERA_ID_RANGE, null, resolution, false)) { h264Encoder.start(); Log.d(TAG, "Network encode started successfully, server: " + config.ip + ":" + config.port + ", resolution: " + width + "x" + height + ", framerate: " + framerate); Timber.d("Network encode started successfully, server: %s:%d, resolution: %dx%d, framerate: %d", config.ip, config.port, width, height, framerate); return 0; // 成功 } else { Log.e(TAG, "Failed to initialize encoder"); Timber.e("Failed to initialize encoder"); h264Encoder = null; return 1; // 失败 } } catch (Exception e) { Log.e(TAG, "Failed to start network encode", e); Timber.e(e, "Failed to start network encode"); h264Encoder = null; return 1; // 失败 } @@ -340,22 +340,22 @@ * 停止编码器 */ private int stopEncoder() { Log.d(TAG, "Stopping encoder"); Timber.d("Stopping encoder"); if (h264Encoder != null) { try { h264Encoder.stop(); h264Encoder.release(); h264Encoder = null; Log.d(TAG, "Encoder stopped successfully"); Timber.d("Encoder stopped successfully"); return 0; // 成功 } catch (Exception e) { Log.e(TAG, "Error stopping encoder", e); Timber.e(e, "Error stopping encoder"); h264Encoder = null; return 1; // 失败 } } else { Log.w(TAG, "Encoder is not running"); Timber.w("Encoder is not running"); return 0; // 成功(没有运行的编码器,视为成功) } } @@ -364,22 +364,22 @@ * 启动文件传输模式(从H264文件读取并网络推送) */ private int startFileTransmit(FileTransmitConfig config) { Log.d(TAG, "Starting file transmit mode"); Timber.d("Starting file transmit mode"); // 如果文件传输器已经在运行,先停止 if (h264FileTransmitter != null) { Log.w(TAG, "File transmitter is already running, stopping it first"); Timber.w("File transmitter is already running, stopping it first"); stopFileTransmitter(); } // 检查必需的配置参数 if (config == null || config.ip == null || config.ip.trim().isEmpty() || config.port <= 0) { Log.e(TAG, "File transmit requires valid ip and port in config"); Timber.e("File transmit requires valid ip and port in config"); return 1; // 失败 } if (config.filePath == null || config.filePath.trim().isEmpty()) { Log.e(TAG, "File transmit requires valid filePath in config"); Timber.e("File transmit requires valid filePath in config"); return 1; // 失败 } @@ -387,7 +387,7 @@ // 检查文件是否存在 File file = new File(config.filePath); if (!file.exists() || !file.isFile()) { Log.e(TAG, "File does not exist: " + config.filePath); Timber.e("File does not exist: %s", config.filePath); return 1; // 失败 } @@ -413,24 +413,24 @@ h264FileTransmitter.setOnTransmitProgressCallback(new H264FileTransmitter.OnTransmitProgressCallback() { @Override public void onProgress(int currentFrame, int totalFrames) { Log.d(TAG, "File transmit progress: frame " + currentFrame + (totalFrames > 0 ? " of " + totalFrames : "")); Timber.d("File transmit progress: frame %d%s", currentFrame, totalFrames > 0 ? " of " + totalFrames : ""); } @Override public void onComplete() { Log.d(TAG, "File transmit completed"); Timber.d("File transmit completed"); } @Override public void onError(String error) { Log.e(TAG, "File transmit error: " + error); Timber.e("File transmit error: %s", error); } }); // 初始化Socket连接 if (!h264FileTransmitter.initialize()) { Log.e(TAG, "Failed to initialize file transmitter socket"); Timber.e("Failed to initialize file transmitter socket"); h264FileTransmitter = null; return 1; // 失败 } @@ -438,19 +438,18 @@ // 开始传输文件 h264FileTransmitter.transmitFile(config.filePath); Log.d(TAG, "File transmit started successfully, file: " + config.filePath + ", server: " + config.ip + ":" + config.port + ", protocol: " + (config.protocolType == JT1076ProtocolHelper.PROTOCOL_TYPE_UDP ? "UDP" : "TCP") + ", framerate: " + framerate); Timber.d("File transmit started successfully, 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) { Log.e(TAG, "Failed to start file transmit", e); Timber.e(e, "Failed to start file transmit"); if (h264FileTransmitter != null) { try { h264FileTransmitter.stop(); } catch (Exception ex) { Log.e(TAG, "Error stopping file transmitter after failure", ex); Timber.e(ex, "Error stopping file transmitter after failure"); } h264FileTransmitter = null; } @@ -462,21 +461,21 @@ * 停止文件传输器 */ private int stopFileTransmitter() { Log.d(TAG, "Stopping file transmitter"); Timber.d("Stopping file transmitter"); if (h264FileTransmitter != null) { try { h264FileTransmitter.stop(); h264FileTransmitter = null; Log.d(TAG, "File transmitter stopped successfully"); Timber.d("File transmitter stopped successfully"); return 0; // 成功 } catch (Exception e) { Log.e(TAG, "Error stopping file transmitter", e); Timber.e(e, "Error stopping file transmitter"); h264FileTransmitter = null; return 1; // 失败 } } else { Log.w(TAG, "File transmitter is not running"); Timber.w("File transmitter is not running"); return 0; // 成功(没有运行的文件传输器,视为成功) } } @@ -488,7 +487,7 @@ * @return 资源列表 */ private List<ResourceInfo> getResourceList(String startTime, String endTime) { Log.d(TAG, "getResourceList called, startTime: " + startTime + ", endTime: " + endTime); Timber.d("getResourceList called, startTime: %s, endTime: %s", startTime, endTime); List<ResourceInfo> resourceList = new ArrayList<>(); @@ -496,13 +495,13 @@ // 扫描输出目录中的H264文件 File dir = new File(outputFileDirectory); if (!dir.exists() || !dir.isDirectory()) { Log.w(TAG, "Output directory does not exist: " + outputFileDirectory); Timber.w("Output directory does not exist: %s", outputFileDirectory); return resourceList; } File[] files = dir.listFiles((dir1, name) -> name.toLowerCase().endsWith(".h264")); if (files == null || files.length == 0) { Log.d(TAG, "No H264 files found in directory"); Timber.d("No H264 files found in directory"); return resourceList; } @@ -511,7 +510,7 @@ Date endDate = parseTime(endTime); if (startDate == null || endDate == null) { Log.e(TAG, "Invalid time format, startTime: " + startTime + ", endTime: " + endTime); Timber.e("Invalid time format, startTime: %s, endTime: %s", startTime, endTime); return resourceList; } @@ -523,11 +522,11 @@ } } Log.d(TAG, "Found " + resourceList.size() + " resources in time range"); Timber.d("Found %d resources in time range", resourceList.size()); return resourceList; } catch (Exception e) { Log.e(TAG, "Error getting resource list", e); Timber.e(e, "Error getting resource list"); return resourceList; } } @@ -578,7 +577,7 @@ return resourceInfo; } catch (Exception e) { Log.e(TAG, "Error creating resource info from file: " + file.getName(), e); Timber.e(e, "Error creating resource info from file: %s", file.getName()); return null; } } @@ -597,7 +596,7 @@ SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss", Locale.CHINA); return format.parse(timeStr); } catch (ParseException e) { Log.e(TAG, "Failed to parse time: " + timeStr, e); Timber.e(e, "Failed to parse time: %s", timeStr); return null; } } app/src/main/java/com/anyun/h264/H264FileTransmitter.java
@@ -1,6 +1,6 @@ package com.anyun.h264; import android.util.Log; import timber.log.Timber; import java.io.File; import java.io.FileInputStream; @@ -119,7 +119,7 @@ public void setFrameRate(int frameRate) { this.frameRate = frameRate > 0 ? frameRate : 25; this.frameInterval = 1000 / this.frameRate; Log.d(TAG, "Frame rate set to: " + this.frameRate + " fps, interval: " + this.frameInterval + " ms"); Timber.d("Frame rate set to: %d fps, interval: %d ms", this.frameRate, this.frameInterval); } /** @@ -135,12 +135,12 @@ */ public boolean initialize() { if (isRunning.get()) { Log.w(TAG, "Transmitter is already running"); Timber.w("Transmitter is already running"); return false; } if (!protocolHelper.initializeSocket()) { Log.e(TAG, "Failed to initialize socket"); Timber.e("Failed to initialize socket"); return false; } @@ -152,7 +152,7 @@ lastIFrameTime = 0; lastFrameTime = 0; Log.d(TAG, "Socket initialized successfully"); Timber.d("Socket initialized successfully"); return true; } @@ -162,13 +162,13 @@ */ public void transmitFile(String filePath) { if (isRunning.get()) { Log.w(TAG, "Transmitter is already running"); Timber.w("Transmitter is already running"); return; } File file = new File(filePath); if (!file.exists() || !file.isFile()) { Log.e(TAG, "File does not exist: " + filePath); Timber.e("File does not exist: %s", filePath); if (progressCallback != null) { progressCallback.onError("File does not exist: " + filePath); } @@ -186,7 +186,7 @@ }); transmitThread.start(); Log.d(TAG, "Started transmitting file: " + filePath); Timber.d("Started transmitting file: %s", filePath); } /** @@ -214,10 +214,10 @@ } if (totalRead != fileData.length) { Log.w(TAG, "File read incomplete, expected: " + fileData.length + ", actual: " + totalRead); Timber.w("File read incomplete, expected: %d, actual: %d", fileData.length, totalRead); } Log.d(TAG, "File read complete, size: " + fileData.length + " bytes"); Timber.d("File read complete, size: %d bytes", fileData.length); // 按帧解析并传输(一个帧包含从一个起始码到下一个起始码之间的所有数据,包括起始码) int offset = 0; @@ -255,19 +255,19 @@ offset = nextFrameStart; } Log.d(TAG, "Transmission complete, total frames: " + frameCount); Timber.d("Transmission complete, total frames: %d", frameCount); if (progressCallback != null) { progressCallback.onComplete(); } } catch (IOException e) { Log.e(TAG, "Error transmitting file", e); Timber.e(e, "Error transmitting file"); if (progressCallback != null) { progressCallback.onError("IO Error: " + e.getMessage()); } } catch (Exception e) { Log.e(TAG, "Unexpected error during transmission", e); Timber.e(e, "Unexpected error during transmission"); if (progressCallback != null) { progressCallback.onError("Error: " + e.getMessage()); } @@ -276,7 +276,7 @@ try { fis.close(); } catch (IOException e) { Log.e(TAG, "Error closing file", e); Timber.e(e, "Error closing file"); } } isRunning.set(false); @@ -464,13 +464,13 @@ Thread.sleep(frameInterval); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.d(TAG, "Transmission interrupted"); Timber.d("Transmission interrupted"); return; } } } catch (Exception e) { Log.e(TAG, "Error transmitting frame", e); Timber.e(e, "Error transmitting frame"); } } @@ -489,7 +489,7 @@ try { transmitThread.join(2000); } catch (InterruptedException e) { Log.e(TAG, "Wait transmit thread error", e); Timber.e(e, "Wait transmit thread error"); } } @@ -498,7 +498,7 @@ protocolHelper.closeSocket(); } Log.d(TAG, "H264 file transmitter stopped"); Timber.d("H264 file transmitter stopped"); } /** app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
@@ -1,12 +1,13 @@ package com.anyun.h264; import android.util.Log; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.nio.ByteBuffer; import timber.log.Timber; /** * JT/T 1076-2016 协议工具类 @@ -78,7 +79,7 @@ */ public void setProtocolType(int protocolType) { if (protocolType != PROTOCOL_TYPE_UDP && protocolType != PROTOCOL_TYPE_TCP) { Log.w(TAG, "Invalid protocol type: " + protocolType + ", using UDP"); Timber.w("Invalid protocol type: " + protocolType + ", using UDP"); protocolType = PROTOCOL_TYPE_UDP; } @@ -92,7 +93,7 @@ } this.protocolType = protocolType; Log.d(TAG, "Protocol type set to: " + (protocolType == PROTOCOL_TYPE_UDP ? "UDP" : "TCP")); Timber.d("Protocol type set to: " + (protocolType == PROTOCOL_TYPE_UDP ? "UDP" : "TCP")); } /** @@ -127,16 +128,16 @@ public boolean initializeUdpSocket() { try { if (serverIp == null || serverIp.isEmpty()) { Log.e(TAG, "Server IP not set"); Timber.e("Server IP not set"); return false; } udpSocket = new DatagramSocket(); serverAddress = InetAddress.getByName(serverIp); Log.d(TAG, "UDP socket initialized, target: " + serverIp + ":" + serverPort); Timber.d("UDP socket initialized, target: " + serverIp + ":" + serverPort); return true; } catch (Exception e) { Log.e(TAG, "Initialize UDP socket failed", e); Timber.e(e,"Initialize UDP socket failed"); return false; } } @@ -147,7 +148,7 @@ public boolean initializeTcpSocket() { try { if (serverIp == null || serverIp.isEmpty()) { Log.e(TAG, "Server IP not set"); Timber.e("Server IP not set"); return false; } @@ -159,26 +160,26 @@ tcpClient.setConnectionListener(new JT1076TcpClient.ConnectionListener() { @Override public void onConnected() { Log.d(TAG, "TCP connection established"); Timber.d("TCP connection established"); } @Override public void onDisconnected() { Log.d(TAG, "TCP connection disconnected"); Timber.d( "TCP connection disconnected"); } @Override public void onError(Throwable cause) { Log.e(TAG, "TCP connection error", cause); Timber.e(cause, "TCP connection error"); } }); } tcpClient.connect(); Log.d(TAG, "TCP socket initializing, target: " + serverIp + ":" + serverPort); Timber.d("TCP socket initializing, target: " + serverIp + ":" + serverPort); return true; } catch (Exception e) { Log.e(TAG, "Initialize TCP socket failed", e); Timber.e(e,"Initialize TCP socket failed"); return false; } } @@ -202,7 +203,7 @@ try { udpSocket.close(); } catch (Exception e) { Log.e(TAG, "Close UDP socket error", e); Timber.e( e,"Close UDP socket error"); } udpSocket = null; } @@ -240,10 +241,10 @@ packet, packet.length, serverAddress, serverPort); udpSocket.send(datagramPacket); } else { Log.w(TAG, "UDP socket not initialized"); Timber.w("UDP socket not initialized"); } } catch (Exception e) { Log.e(TAG, "Send UDP packet error", e); Timber.e(e,"Send UDP packet error"); } } @@ -254,7 +255,7 @@ if (tcpClient != null && tcpClient.isConnected()) { tcpClient.sendPacket(packet); } else { Log.w(TAG, "TCP socket not connected"); Timber.w("TCP socket not connected"); } } app/src/main/java/com/anyun/h264/MainActivity.kt
@@ -1,7 +1,7 @@ package com.anyun.h264 import android.os.Bundle import android.util.Log import timber.log.Timber import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -52,7 +52,7 @@ private fun startH264Encoder(): Boolean { if (h264Encoder != null) { Log.w("MainActivity", "H264Encoder is already running") Timber.w("H264Encoder is already running") return false } @@ -80,16 +80,16 @@ if (h264Encoder?.initialize(cameraIdRange, null, resolution, false) == true) { h264Encoder?.start() Log.d("MainActivity", "H264Encoder started successfully") Log.d("MainActivity", "Output file: ${outputFile.absolutePath}") Timber.d("H264Encoder started successfully") Timber.d("Output file: %s", outputFile.absolutePath) return true } else { Log.e("MainActivity", "Failed to initialize H264Encoder") Timber.e("Failed to initialize H264Encoder") h264Encoder = null return false } } catch (e: Exception) { Log.e("MainActivity", "Failed to start H264Encoder", e) Timber.e(e, "Failed to start H264Encoder") h264Encoder = null return false } @@ -99,9 +99,9 @@ h264Encoder?.let { encoder -> try { encoder.stop() Log.d("MainActivity", "H264Encoder stopped") Timber.d("H264Encoder stopped") } catch (e: Exception) { Log.e("MainActivity", "Failed to stop H264Encoder", e) Timber.e(e, "Failed to stop H264Encoder") } h264Encoder = null } gradle/libs.versions.toml
@@ -9,6 +9,7 @@ activityCompose = "1.8.0" composeBom = "2024.04.01" netty = "4.1.48.Final" timber = "4.7.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -26,6 +27,7 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } netty-all = { group = "io.netty", name = "netty-all", version.ref = "netty" } timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }