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