From 0ff46cc3489bf028802c9ef46f54bf2b77c23fb4 Mon Sep 17 00:00:00 2001
From: Dana <Dana_Lee1016@126.com>
Date: 星期日, 30 十一月 2025 16:22:32 +0800
Subject: [PATCH] 1.h264文件以rtp sendPacket

---
 app/src/main/java/com/anyun/h264/H264FileTransmitter.java |  511 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 511 insertions(+), 0 deletions(-)

diff --git a/app/src/main/java/com/anyun/h264/H264FileTransmitter.java b/app/src/main/java/com/anyun/h264/H264FileTransmitter.java
new file mode 100644
index 0000000..51299d0
--- /dev/null
+++ b/app/src/main/java/com/anyun/h264/H264FileTransmitter.java
@@ -0,0 +1,511 @@
+package com.anyun.h264;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * H264鏂囦欢浼犺緭鍣�
+ * 浠嶩264鏂囦欢璇诲彇鏁版嵁锛屾寜鐓T/T 1076-2016鍗忚閫氳繃TCP/UDP浼犺緭
+ * 
+ * 浣跨敤绀轰緥锛�
+ * <pre>
+ * // 鍒涘缓浼犺緭鍣�
+ * H264FileTransmitter transmitter = new H264FileTransmitter();
+ * 
+ * // 璁剧疆鏈嶅姟鍣ㄥ湴鍧�鍜屽崗璁被鍨�
+ * transmitter.setServerAddress("192.168.1.100", 8888);
+ * transmitter.setProtocolType(JT1076ProtocolHelper.PROTOCOL_TYPE_TCP); // 鎴� PROTOCOL_TYPE_UDP
+ * 
+ * // 璁剧疆鍗忚鍙傛暟
+ * transmitter.setProtocolParams("013120122580", (byte)1);
+ * 
+ * // 璁剧疆甯х巼锛堢敤浜庤绠楁椂闂存埑闂撮殧锛�
+ * transmitter.setFrameRate(25);
+ * 
+ * // 鍒濆鍖朣ocket
+ * if (transmitter.initialize()) {
+ *     // 寮�濮嬩紶杈撴枃浠�
+ *     transmitter.transmitFile("/path/to/video.h264");
+ * }
+ * 
+ * // 鍋滄浼犺緭
+ * transmitter.stop();
+ * </pre>
+ */
+public class H264FileTransmitter {
+    private static final String TAG = "H264FileTransmitter";
+    
+    // H264 NAL璧峰鐮�
+    private static final byte[] START_CODE_3 = {0x00, 0x00, 0x01};
+    private static final byte[] START_CODE_4 = {0x00, 0x00, 0x00, 0x01};
+    
+    // JT/T 1076-2016 鍗忚宸ュ叿绫�
+    private JT1076ProtocolHelper protocolHelper;
+    
+    // 浼犺緭鎺у埗
+    private AtomicBoolean isRunning = new AtomicBoolean(false);
+    private Thread transmitThread;
+    
+    // 鍙傛暟閰嶇疆
+    private int frameRate = 25; // 甯х巼锛岀敤浜庤绠楁椂闂存埑闂撮殧
+    private long frameInterval = 1000 / 25; // 甯ч棿闅旓紙姣锛�
+    
+    // 鏃堕棿鎴崇鐞�
+    private long lastIFrameTime = 0; // 涓婁竴涓狪甯ф椂闂�
+    private long lastFrameTime = 0; // 涓婁竴甯ф椂闂�
+    private long baseTimestamp = 0; // 鍩哄噯鏃堕棿鎴�
+    
+    /**
+     * 浼犺緭杩涘害鍥炶皟鎺ュ彛
+     */
+    public interface OnTransmitProgressCallback {
+        /**
+         * 浼犺緭杩涘害鍥炶皟
+         * @param currentFrame 褰撳墠甯у簭鍙凤紙浠�1寮�濮嬶級
+         * @param totalFrames 鎬诲抚鏁帮紙濡傛灉鏈煡鍒欎负-1锛�
+         */
+        void onProgress(int currentFrame, int totalFrames);
+        
+        /**
+         * 浼犺緭瀹屾垚鍥炶皟
+         */
+        void onComplete();
+        
+        /**
+         * 浼犺緭閿欒鍥炶皟
+         * @param error 閿欒淇℃伅
+         */
+        void onError(String error);
+    }
+    private OnTransmitProgressCallback progressCallback;
+    
+    public H264FileTransmitter() {
+        this.protocolHelper = new JT1076ProtocolHelper();
+        // 榛樿浣跨敤TCP鍗忚
+        protocolHelper.setProtocolType(JT1076ProtocolHelper.PROTOCOL_TYPE_TCP);
+    }
+    
+    /**
+     * 璁剧疆鏈嶅姟鍣ㄥ湴鍧�
+     */
+    public void setServerAddress(String ip, int port) {
+        protocolHelper.setServerAddress(ip, port);
+    }
+    
+    /**
+     * 璁剧疆浼犺緭鍗忚绫诲瀷锛圲DP鎴朤CP锛�
+     * @param protocolType PROTOCOL_TYPE_UDP 鎴� PROTOCOL_TYPE_TCP
+     */
+    public void setProtocolType(int protocolType) {
+        protocolHelper.setProtocolType(protocolType);
+    }
+    
+    /**
+     * 璁剧疆SIM鍗″彿鍜岄�昏緫閫氶亾鍙�
+     */
+    public void setProtocolParams(String simCardNumber, byte logicalChannelNumber) {
+        protocolHelper.setProtocolParams(simCardNumber, logicalChannelNumber);
+    }
+    
+    /**
+     * 璁剧疆甯х巼锛堢敤浜庤绠楁椂闂存埑闂撮殧锛�
+     * @param frameRate 甯х巼锛坒ps锛�
+     */
+    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");
+    }
+    
+    /**
+     * 璁剧疆浼犺緭杩涘害鍥炶皟
+     */
+    public void setOnTransmitProgressCallback(OnTransmitProgressCallback callback) {
+        this.progressCallback = callback;
+    }
+    
+    /**
+     * 鍒濆鍖朣ocket杩炴帴
+     * @return 鏄惁鎴愬姛
+     */
+    public boolean initialize() {
+        if (isRunning.get()) {
+            Log.w(TAG, "Transmitter is already running");
+            return false;
+        }
+        
+        if (!protocolHelper.initializeSocket()) {
+            Log.e(TAG, "Failed to initialize socket");
+            return false;
+        }
+        
+        // 閲嶇疆搴忓彿
+        protocolHelper.resetSequenceNumber();
+        
+        // 鍒濆鍖栨椂闂存埑
+        baseTimestamp = System.currentTimeMillis();
+        lastIFrameTime = 0;
+        lastFrameTime = 0;
+        
+        Log.d(TAG, "Socket initialized successfully");
+        return true;
+    }
+    
+    /**
+     * 寮�濮嬩紶杈揌264鏂囦欢
+     * @param filePath H264鏂囦欢璺緞
+     */
+    public void transmitFile(String filePath) {
+        if (isRunning.get()) {
+            Log.w(TAG, "Transmitter is already running");
+            return;
+        }
+        
+        File file = new File(filePath);
+        if (!file.exists() || !file.isFile()) {
+            Log.e(TAG, "File does not exist: " + filePath);
+            if (progressCallback != null) {
+                progressCallback.onError("File does not exist: " + filePath);
+            }
+            return;
+        }
+        
+        isRunning.set(true);
+        
+        // 鍚姩浼犺緭绾跨▼
+        transmitThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                transmitFileInternal(file);
+            }
+        });
+        transmitThread.start();
+        
+        Log.d(TAG, "Started transmitting file: " + filePath);
+    }
+    
+    /**
+     * 浼犺緭鏂囦欢鐨勫唴閮ㄥ疄鐜�
+     */
+    private void transmitFileInternal(File file) {
+        FileInputStream fis = null;
+        int frameCount = 0;
+        
+        try {
+            fis = new FileInputStream(file);
+            
+            // 璇诲彇鏁翠釜鏂囦欢鍒板唴瀛�
+            // 娉ㄦ剰锛氬浜庡ぇ鏂囦欢锛屽彲浠ユ敼涓烘祦寮忚鍙栵紝浣嗕负绠�鍖栧疄鐜帮紝杩欓噷浣跨敤涓�娆℃�ц鍙�
+            long fileSize = file.length();
+            if (fileSize > Integer.MAX_VALUE) {
+                throw new IOException("File too large: " + fileSize + " bytes");
+            }
+            
+            byte[] fileData = new byte[(int) fileSize];
+            int bytesRead = 0;
+            int totalRead = 0;
+            while (totalRead < fileData.length && (bytesRead = fis.read(fileData, totalRead, fileData.length - totalRead)) > 0) {
+                totalRead += bytesRead;
+            }
+            
+            if (totalRead != fileData.length) {
+                Log.w(TAG, "File read incomplete, expected: " + fileData.length + ", actual: " + totalRead);
+            }
+            
+            Log.d(TAG, "File read complete, size: " + fileData.length + " bytes");
+            
+            // 鎸夊抚瑙f瀽骞朵紶杈擄紙涓�涓抚鍖呭惈浠庝竴涓捣濮嬬爜鍒颁笅涓�涓捣濮嬬爜涔嬮棿鐨勬墍鏈夋暟鎹紝鍖呮嫭璧峰鐮侊級
+            int offset = 0;
+            while (offset < fileData.length && isRunning.get()) {
+                // 鏌ユ壘涓嬩竴涓抚鐨勮捣濮嬩綅缃�
+                int nextFrameStart = findNextFrameStart(fileData, offset);
+                if (nextFrameStart < 0) {
+                    // 娌℃湁鎵惧埌涓嬩竴涓捣濮嬬爜锛屽綋鍓峯ffset鍒版枃浠舵湯灏炬槸涓�涓畬鏁寸殑甯�
+                    if (offset < fileData.length) {
+                        byte[] frameData = Arrays.copyOfRange(fileData, offset, fileData.length);
+                        transmitFrame(frameData, frameCount);
+                        frameCount++;
+                        
+                        // 閫氱煡杩涘害
+                        if (progressCallback != null) {
+                            progressCallback.onProgress(frameCount, -1);
+                        }
+                    }
+                    break;
+                }
+                
+                // 鎻愬彇褰撳墠甯ф暟鎹紙鍖呭惈璧峰鐮侊級
+                if (nextFrameStart > offset) {
+                    byte[] frameData = Arrays.copyOfRange(fileData, offset, nextFrameStart);
+                    transmitFrame(frameData, frameCount);
+                    frameCount++;
+                    
+                    // 閫氱煡杩涘害
+                    if (progressCallback != null) {
+                        progressCallback.onProgress(frameCount, -1);
+                    }
+                }
+                
+                // 绉诲姩鍒颁笅涓�涓抚鐨勮捣濮嬩綅缃�
+                offset = nextFrameStart;
+            }
+            
+            Log.d(TAG, "Transmission complete, total frames: " + frameCount);
+            
+            if (progressCallback != null) {
+                progressCallback.onComplete();
+            }
+            
+        } catch (IOException e) {
+            Log.e(TAG, "Error transmitting file", e);
+            if (progressCallback != null) {
+                progressCallback.onError("IO Error: " + e.getMessage());
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unexpected error during transmission", e);
+            if (progressCallback != null) {
+                progressCallback.onError("Error: " + e.getMessage());
+            }
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Error closing file", e);
+                }
+            }
+            isRunning.set(false);
+        }
+    }
+    
+    /**
+     * 鏌ユ壘涓嬩竴涓抚鐨勮捣濮嬩綅缃紙涓嬩竴涓捣濮嬬爜鐨勪綅缃級
+     * @param data 鏂囦欢鏁版嵁
+     * @param currentFrameStart 褰撳墠甯х殑璧峰浣嶇疆锛堣捣濮嬬爜浣嶇疆锛�
+     * @return 涓嬩竴涓抚鐨勮捣濮嬬爜浣嶇疆锛屽鏋滄湭鎵惧埌杩斿洖-1
+     */
+    private int findNextFrameStart(byte[] data, int currentFrameStart) {
+        if (currentFrameStart >= data.length) {
+            return -1;
+        }
+        
+        // 璺宠繃褰撳墠甯х殑璧峰鐮�
+        int offset = currentFrameStart;
+        if (isStartCodeAt(data, offset)) {
+            offset += getStartCodeLength(data, offset);
+        }
+        
+        // 鏌ユ壘涓嬩竴涓捣濮嬬爜
+        for (int i = offset; i < data.length - 3; i++) {
+            if (isStartCodeAt(data, i)) {
+                return i;
+            }
+        }
+        
+        return -1;
+    }
+    
+    /**
+     * 妫�鏌ユ寚瀹氫綅缃槸鍚︿负璧峰鐮�
+     */
+    private boolean isStartCodeAt(byte[] data, int offset) {
+        if (offset + 3 > data.length) {
+            return false;
+        }
+        
+        // 妫�鏌�4瀛楄妭璧峰鐮�
+        if (offset + 4 <= data.length) {
+            if (data[offset] == 0x00 && data[offset + 1] == 0x00 &&
+                data[offset + 2] == 0x00 && data[offset + 3] == 0x01) {
+                return true;
+            }
+        }
+        
+        // 妫�鏌�3瀛楄妭璧峰鐮侊紙纭繚鍓嶉潰涓嶆槸0x00锛�
+        if (offset == 0 || data[offset - 1] != 0x00) {
+            if (data[offset] == 0x00 && data[offset + 1] == 0x00 &&
+                data[offset + 2] == 0x01) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 鑾峰彇璧峰鐮侀暱搴�
+     */
+    private int getStartCodeLength(byte[] data, int offset) {
+        if (offset + 4 <= data.length &&
+            data[offset] == 0x00 && data[offset + 1] == 0x00 &&
+            data[offset + 2] == 0x00 && data[offset + 3] == 0x01) {
+            return 4;
+        }
+        return 3;
+    }
+    
+    /**
+     * 鍒ゆ柇甯ф暟鎹槸鍚︿负I甯э紙IDR锛�
+     * @param frameData 甯ф暟鎹紙鍖呭惈璧峰鐮侊級
+     * @return 鏄惁涓篒甯�
+     */
+    private boolean isIFrame(byte[] frameData) {
+        if (frameData == null || frameData.length < 5) {
+            return false;
+        }
+        
+        // 鏌ユ壘甯т腑鎵�鏈夌殑NAL鍗曞厓锛屾鏌ユ槸鍚﹀寘鍚獻DR甯�
+        int offset = 0;
+        boolean hasSpsPps = false;
+        
+        while (offset < frameData.length - 3) {
+            // 妫�鏌ュ綋鍓嶄綅缃槸鍚︿负璧峰鐮�
+            if (isStartCodeAt(frameData, offset)) {
+                // 璺宠繃璧峰鐮�
+                int startCodeLen = getStartCodeLength(frameData, offset);
+                int nalStart = offset + startCodeLen;
+                
+                if (nalStart < frameData.length) {
+                    // 鑾峰彇NAL绫诲瀷锛堢涓�涓瓧鑺傜殑浣�5浣嶏級
+                    int nalType = frameData[nalStart] & 0x1F;
+                    
+                    // NAL绫诲瀷5 = IDR (Instantaneous Decoder Refresh) 鍏抽敭甯�
+                    if (nalType == 5) {
+                        return true; // 鎵惧埌IDR甯э紝纭畾鏄疘甯�
+                    }
+                    
+                    // NAL绫诲瀷7 = SPS, 绫诲瀷8 = PPS
+                    if (nalType == 7 || nalType == 8) {
+                        hasSpsPps = true;
+                    }
+                }
+                
+                // 绉诲姩鍒颁笅涓�涓彲鑳界殑浣嶇疆缁х画鏌ユ壘
+                offset = nalStart + 1;
+            } else {
+                offset++;
+            }
+        }
+        
+        // 濡傛灉娌℃湁鎵惧埌IDR锛屼絾鍖呭惈SPS/PPS锛屼篃璁や负鏄叧閿抚鐩稿叧鐨勬暟鎹�
+        // 锛堟湁浜涚紪鐮佸櫒鍙兘灏哠PS/PPS鍗曠嫭浣滀负涓�涓�"甯�"鍙戦�侊級
+        return hasSpsPps;
+    }
+    
+    /**
+     * 浼犺緭涓�涓畬鏁寸殑甯ф暟鎹紙绫讳技H264Encoder鐨勬柟寮忥級
+     * @param frameData 甯ф暟鎹紙鍖呭惈璧峰鐮侊紝涓嶩264Encoder杈撳嚭鐨勬牸寮忎竴鑷达級
+     * @param frameIndex 甯у簭鍙�
+     */
+    private void transmitFrame(byte[] frameData, int frameIndex) {
+        if (frameData == null || frameData.length == 0) {
+            return;
+        }
+        
+        try {
+            // 鍒ゆ柇甯х被鍨�
+            boolean isKeyFrame = isIFrame(frameData);
+            
+            // 璁$畻鏃堕棿鎴�
+            long timestamp = baseTimestamp + (frameIndex * frameInterval);
+            
+            // 璁$畻鏃堕棿闂撮殧
+            long lastIFrameInterval = (lastIFrameTime > 0) ? (timestamp - lastIFrameTime) : 0;
+            long lastFrameInterval = (lastFrameTime > 0) ? (timestamp - lastFrameTime) : frameInterval;
+            
+            if (isKeyFrame) {
+                lastIFrameTime = timestamp;
+            }
+            lastFrameTime = timestamp;
+            
+            // 鍒ゆ柇甯х被鍨嬶紙鐢ㄤ簬鍗忚锛�
+            int dataType = isKeyFrame ? JT1076ProtocolHelper.DATA_TYPE_I_FRAME :
+                    JT1076ProtocolHelper.DATA_TYPE_P_FRAME;
+            
+            // 鍒嗗寘鍙戦�侊紙濡傛灉鏁版嵁瓒呰繃鏈�澶у寘澶у皬锛�
+            int offset = 0;
+            int totalPackets = (int) Math.ceil((double) frameData.length / JT1076ProtocolHelper.MAX_PACKET_SIZE);
+            
+            for (int i = 0; i < totalPackets; i++) {
+                int packetDataSize = Math.min(JT1076ProtocolHelper.MAX_PACKET_SIZE, frameData.length - offset);
+                byte[] packetData = Arrays.copyOfRange(frameData, offset, offset + packetDataSize);
+                
+                // 纭畾鍒嗗寘鏍囪
+                int packetMark;
+                if (totalPackets == 1) {
+                    packetMark = JT1076ProtocolHelper.PACKET_MARK_ATOMIC;
+                } else if (i == 0) {
+                    packetMark = JT1076ProtocolHelper.PACKET_MARK_FIRST;
+                } else if (i == totalPackets - 1) {
+                    packetMark = JT1076ProtocolHelper.PACKET_MARK_LAST;
+                } else {
+                    packetMark = JT1076ProtocolHelper.PACKET_MARK_MIDDLE;
+                }
+                
+                // 鍒涘缓RTP鍖�
+                byte[] rtpPacket = protocolHelper.createVideoRtpPacket(
+                        packetData, timestamp, dataType, packetMark,
+                        lastIFrameInterval, lastFrameInterval);
+                
+                // 鍙戦�丷TP鍖咃紙UDP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
+                protocolHelper.sendPacket(rtpPacket);
+                
+                offset += packetDataSize;
+            }
+            
+            // 鎺у埗鍙戦�侀�熺巼锛堟ā鎷熷抚鐜囷級
+            if (frameInterval > 0) {
+                try {
+                    Thread.sleep(frameInterval);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    Log.d(TAG, "Transmission interrupted");
+                    return;
+                }
+            }
+            
+        } catch (Exception e) {
+            Log.e(TAG, "Error transmitting frame", e);
+        }
+    }
+    
+    /**
+     * 鍋滄浼犺緭
+     */
+    public void stop() {
+        if (!isRunning.get()) {
+            return;
+        }
+        
+        isRunning.set(false);
+        
+        // 绛夊緟浼犺緭绾跨▼缁撴潫
+        if (transmitThread != null) {
+            try {
+                transmitThread.join(2000);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Wait transmit thread error", e);
+            }
+        }
+        
+        // 鍏抽棴Socket
+        if (protocolHelper != null) {
+            protocolHelper.closeSocket();
+        }
+        
+        Log.d(TAG, "H264 file transmitter stopped");
+    }
+    
+    /**
+     * 閲婃斁璧勬簮
+     */
+    public void release() {
+        stop();
+    }
+}
+

--
Gitblit v1.8.0