From 079bfefba9bc3a96a6bd6f93eb8c26e8397e3949 Mon Sep 17 00:00:00 2001
From: Dana <Dana_Lee1016@126.com>
Date: 星期二, 02 十二月 2025 14:03:11 +0800
Subject: [PATCH] 1.tijiao  sps pps和I帧一起发

---
 app/src/main/java/com/anyun/h264/H264Encoder.java          |  166 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java |    2 
 2 files changed, 156 insertions(+), 12 deletions(-)

diff --git a/app/src/main/java/com/anyun/h264/H264Encoder.java b/app/src/main/java/com/anyun/h264/H264Encoder.java
index 2aa6e5d..2a2bab9 100644
--- a/app/src/main/java/com/anyun/h264/H264Encoder.java
+++ b/app/src/main/java/com/anyun/h264/H264Encoder.java
@@ -64,9 +64,9 @@
     // 缂栫爜鍙傛暟
     private int width = 640;
     private int height = 480;
-    private int frameRate = 25;
+    private int frameRate = 15;
     private int bitrate = 2000000; // 2Mbps
-    private int iFrameInterval = 1; // I甯ч棿闅旓紙绉掞級
+    private int iFrameInterval = 2; // I甯ч棿闅旓紙绉掞級
 
     // JT/T 1076-2016 鍗忚宸ュ叿绫�
     private JT1076ProtocolHelper protocolHelper;
@@ -81,6 +81,10 @@
 
     // 缃戠粶浼犺緭鎺у埗
     private boolean enableNetworkTransmission = true; // 鏄惁鍚敤TCP/UDP缃戠粶浼犺緭
+
+    // SPS/PPS 缂撳瓨锛堢敤浜庣綉缁滀紶杈擄級
+    private byte[] spsBuffer = null; // SPS 缂撳瓨
+    private byte[] ppsBuffer = null; // PPS 缂撳瓨
 
     // 缂栫爜鍥炶皟
     public interface OnFrameEncodedCallback {
@@ -315,6 +319,10 @@
         encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
         encoder.start();
 
+        // 娓呯悊 SPS/PPS 缂撳瓨
+        spsBuffer = null;
+        ppsBuffer = null;
+
         Timber.d( "H264 encoder initialized");
     }
 
@@ -502,22 +510,21 @@
             while (outputBufferIndex >= 0) {
                 ByteBuffer outputBuffer = encoder.getOutputBuffer(outputBufferIndex);
                 if (outputBuffer != null && bufferInfo.size > 0) {
-                    // 妫�鏌ユ槸鍚︿负鍏抽敭甯�
-                    boolean isKeyFrame = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0;
                     // 澶嶅埗缂栫爜鏁版嵁
-                    byte[] encodedData = new byte[bufferInfo.size];
+                    byte[] nalUnit = new byte[bufferInfo.size];
                     outputBuffer.position(bufferInfo.offset);
-                    outputBuffer.get(encodedData, 0, bufferInfo.size);
+                    outputBuffer.get(nalUnit, 0, bufferInfo.size);
 
-                    // 鍐欏叆鏂囦欢
-                    writeToFile(encodedData, isKeyFrame);
+                    // 瑙f瀽骞跺鐞� NAL 鍗曞厓
+                    processNalUnit(nalUnit, timestamp);
 
-                    // 鍙戦�佺紪鐮佹暟鎹�
-                    sendEncodedData(encodedData, timestamp, isKeyFrame);
+                    // 鍐欏叆鏂囦欢锛堜繚鎸佸師鏈夐�昏緫锛�
+                    boolean isKeyFrame = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0;
+                    writeToFile(nalUnit, isKeyFrame);
 
                     // 鍥炶皟
                     if (callback != null) {
-                        callback.onFrameEncoded(encodedData, isKeyFrame);
+                        callback.onFrameEncoded(nalUnit, isKeyFrame);
                     }
                 }
 
@@ -528,6 +535,139 @@
         } catch (Exception e) {
             Timber.e(e,"Encode frame error");
         }
+    }
+
+    /**
+     * 澶勭悊 NAL 鍗曞厓锛岃瘑鍒被鍨嬪苟鍒嗗埆澶勭悊
+     */
+    private void processNalUnit(byte[] data, long timestamp) {
+        if (data == null || data.length < 5) {
+            return;
+        }
+
+        // MediaCodec 杈撳嚭鐨勬暟鎹槸 Annex-B 鏍煎紡锛屽彲鑳藉寘鍚涓� NAL 鍗曞厓
+        // 姣忎釜 NAL 鍗曞厓浠� 0x00000001 鎴� 0x000001 寮�澶�
+        int offset = 0;
+        while (offset < data.length) {
+            // 鏌ユ壘璧峰鐮�
+            int startCodePos = findStartCode(data, offset);
+            if (startCodePos == -1) {
+                break; // 娌℃湁鎵惧埌璧峰鐮侊紝缁撴潫
+            }
+
+            // 璺宠繃璧峰鐮侊紝鎵惧埌 NAL 鍗曞厓鏁版嵁
+            int nalStart = startCodePos;
+            int startCodeLength = (startCodePos + 2 < data.length && 
+                                   data[startCodePos] == 0x00 && 
+                                   data[startCodePos + 1] == 0x00 && 
+                                   data[startCodePos + 2] == 0x01) ? 3 : 4;
+            int nalDataStart = nalStart + startCodeLength;
+
+            // 鏌ユ壘涓嬩竴涓捣濮嬬爜锛岀‘瀹氬綋鍓� NAL 鍗曞厓鐨勭粨鏉熶綅缃�
+            int nextStartCodePos = findStartCode(data, nalDataStart);
+            int nalDataEnd = (nextStartCodePos == -1) ? data.length : nextStartCodePos;
+            int nalDataLength = nalDataEnd - nalDataStart;
+
+            if (nalDataLength > 0 && nalDataStart < data.length) {
+                // 鎻愬彇 NAL 鍗曞厓鏁版嵁锛堝寘鍚捣濮嬬爜锛�
+                byte[] nalUnit = new byte[nalDataLength + startCodeLength];
+                System.arraycopy(data, nalStart, nalUnit, 0, startCodeLength);
+                System.arraycopy(data, nalDataStart, nalUnit, startCodeLength, nalDataLength);
+
+                // 鑾峰彇 NAL 绫诲瀷锛堣捣濮嬬爜鍚庣殑绗竴涓瓧鑺傜殑浣�5浣嶏級
+                int nalType = (nalUnit[startCodeLength] & 0x1F);
+
+                // 鏍规嵁 NAL 绫诲瀷澶勭悊
+                switch (nalType) {
+                    case 7: // SPS
+                        spsBuffer = nalUnit.clone();
+                        Timber.d("SPS cached, size: %d", spsBuffer.length);
+                        break;
+
+                    case 8: // PPS
+                        ppsBuffer = nalUnit.clone();
+                        Timber.d("PPS cached, size: %d", ppsBuffer.length);
+                        break;
+
+                    case 5: // IDR 甯� (鍏抽敭甯�)
+                        // 鍏抽敭甯у繀椤讳笌 SPS銆丳PS 涓�璧峰彂閫�
+                        if (spsBuffer != null && ppsBuffer != null) {
+                            // 灏� SPS, PPS, IDR 甯ф暟鎹粍鍚堟垚涓�涓畬鏁寸殑"甯ф暟鎹崟鍏�"
+                            byte[] frameData = combineFrameData(spsBuffer, ppsBuffer, nalUnit);
+                            // 鍙戦�佺粍鍚堝悗鐨勬暟鎹�
+                            sendEncodedData(frameData, timestamp, true);
+                        } else {
+                            Timber.w("Received IDR frame, but SPS or PPS not ready, dropping");
+                            // 濡傛灉娌℃湁 SPS/PPS锛屼粛鐒跺彂閫� IDR 甯э紙鍙兘鍖呭惈鍦ㄦ暟鎹腑锛�
+                            sendEncodedData(nalUnit, timestamp, true);
+                        }
+                        break;
+
+                    case 1: // 闈� IDR 甯� (P 甯�)
+                        // 鐩存帴鍙戦�� P 甯�
+                        sendEncodedData(nalUnit, timestamp, false);
+                        break;
+
+                    default:
+                        // 鍏朵粬 NALU 绫诲瀷锛堝 SEI 绛夛級锛屾牴鎹崗璁喅瀹氭槸鍚﹀鐞�
+                        // 杩欓噷鍙互閫夋嫨鍙戦�佹垨蹇界暐
+                        Timber.d("NAL unit type %d, size: %d", nalType, nalUnit.length);
+                        break;
+                }
+            }
+
+            // 绉诲姩鍒颁笅涓�涓彲鑳界殑璧峰浣嶇疆
+            offset = nalDataEnd;
+        }
+    }
+
+    /**
+     * 鏌ユ壘璧峰鐮佷綅缃紙0x00000001 鎴� 0x000001锛�
+     * @param data 鏁版嵁鏁扮粍
+     * @param startPos 寮�濮嬫悳绱㈢殑浣嶇疆
+     * @return 璧峰鐮佷綅缃紝濡傛灉鏈壘鍒拌繑鍥� -1
+     */
+    private int findStartCode(byte[] data, int startPos) {
+        for (int i = startPos; i < data.length - 2; i++) {
+            if (data[i] == 0x00 && data[i + 1] == 0x00) {
+                if (i + 2 < data.length && data[i + 2] == 0x01) {
+                    return i; // 鎵惧埌 0x000001
+                }
+                if (i + 3 < data.length && data[i + 2] == 0x00 && data[i + 3] == 0x01) {
+                    return i; // 鎵惧埌 0x00000001
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * 缁勫悎 SPS銆丳PS 鍜� IDR 甯ф暟鎹�
+     * @param sps SPS 鏁版嵁锛堝寘鍚捣濮嬬爜锛�
+     * @param pps PPS 鏁版嵁锛堝寘鍚捣濮嬬爜锛�
+     * @param idr IDR 甯ф暟鎹紙鍖呭惈璧峰鐮侊級
+     * @return 缁勫悎鍚庣殑鏁版嵁
+     */
+    private byte[] combineFrameData(byte[] sps, byte[] pps, byte[] idr) {
+        int totalLength = sps.length + pps.length + idr.length;
+        byte[] combined = new byte[totalLength];
+        int offset = 0;
+
+        // 澶嶅埗 SPS
+        System.arraycopy(sps, 0, combined, offset, sps.length);
+        offset += sps.length;
+
+        // 澶嶅埗 PPS
+        System.arraycopy(pps, 0, combined, offset, pps.length);
+        offset += pps.length;
+
+        // 澶嶅埗 IDR 甯�
+        System.arraycopy(idr, 0, combined, offset, idr.length);
+
+        Timber.d("Combined SPS/PPS/IDR frame, total size: %d (SPS: %d, PPS: %d, IDR: %d)", 
+                 totalLength, sps.length, pps.length, idr.length);
+
+        return combined;
     }
 
     /**
@@ -664,6 +804,10 @@
         // 鍏抽棴鏂囦欢杈撳嚭
         closeFileOutput();
 
+        // 娓呯悊 SPS/PPS 缂撳瓨
+        spsBuffer = null;
+        ppsBuffer = null;
+
         Timber.d("H264 encoder stopped");
     }
 
diff --git a/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java b/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
index ebe7c11..6e53e5d 100644
--- a/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
+++ b/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
@@ -35,7 +35,7 @@
     public static final int PACKET_MARK_MIDDLE = 0x03; // 涓棿鍖�
     
     // RTP璐熻浇绫诲瀷
-    public static final int RTP_PAYLOAD_TYPE_VIDEO = 96;  // 瑙嗛璐熻浇绫诲瀷
+    public static final int RTP_PAYLOAD_TYPE_VIDEO = 98;  // 瑙嗛璐熻浇绫诲瀷
     public static final int RTP_PAYLOAD_TYPE_AUDIO = 97;  // 闊抽璐熻浇绫诲瀷
     
     // 浼犺緭鍗忚绫诲瀷

--
Gitblit v1.8.0