网站通话使用webrtc,新增ffmpeg
This commit is contained in:
@@ -189,14 +189,53 @@ func (r *ChunkReader) ReadMessage() (*RTMPMessage, error) {
|
||||
toRead = remaining
|
||||
}
|
||||
|
||||
// toRead=0 说明 MessageLength=0,可能是 fmt=3 没有有效的 prevHeader
|
||||
// 记录详细日志以便调试,然后跳过此 chunk 继续尝试读取下一个
|
||||
// toRead=0 说明 MessageLength=0,可能是 fmt=2/3 没有有效的 prevHeader
|
||||
// 尝试通过搜索下一个 fmt=0 chunk 来重新同步
|
||||
if toRead == 0 {
|
||||
log.Printf("⚠️ [RTMP Protocol] toRead=0 on csid=%d fmt=%d msgLen=%d bytesRead=%d, 跳过此 chunk",
|
||||
header.ChunkStreamID, header.Format, state.header.MessageLength, state.bytesRead)
|
||||
// 清除此 csid 的无效状态,等待有效的 fmt=0 重新开始
|
||||
log.Printf("⚠️ [RTMP Protocol] csid=%d fmt=%d msgLen=0, 尝试重新同步...",
|
||||
header.ChunkStreamID, header.Format)
|
||||
|
||||
// 清除此 csid 的状态
|
||||
delete(r.messageBuffer, header.ChunkStreamID)
|
||||
continue // 继续尝试读取下一个 chunk,而不是返回错误
|
||||
|
||||
// 尝试寻找下一个有效的 chunk 起始位置
|
||||
// 读取并丢弃字节,直到找到可能的 fmt=0 chunk header
|
||||
maxScanBytes := 4096 // 最多扫描 4KB
|
||||
scanned := 0
|
||||
foundSync := false
|
||||
|
||||
for scanned < maxScanBytes {
|
||||
b, err := r.reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("重新同步时读取失败: %w", err)
|
||||
}
|
||||
scanned++
|
||||
|
||||
// 检查是否可能是 fmt=0 的 chunk header
|
||||
// fmt=0 的 basic header 第一个字节: 高 2 位为 00
|
||||
potentialFmt := (b >> 6) & 0x03
|
||||
potentialCsid := uint32(b & 0x3F)
|
||||
|
||||
if potentialFmt == 0 && potentialCsid >= 2 && potentialCsid < 64 {
|
||||
// 可能找到了 fmt=0 chunk,尝试验证
|
||||
// 先放回这个字节
|
||||
if err := r.reader.UnreadByte(); err != nil {
|
||||
return nil, fmt.Errorf("UnreadByte 失败: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("ℹ️ [RTMP Protocol] 可能找到同步点: 跳过了 %d 字节, 潜在 csid=%d",
|
||||
scanned-1, potentialCsid)
|
||||
foundSync = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundSync {
|
||||
log.Printf("❌ [RTMP Protocol] 扫描 %d 字节后仍未找到同步点", scanned)
|
||||
return nil, fmt.Errorf("RTMP 协议错误: 无法重新同步")
|
||||
}
|
||||
|
||||
continue // 重新尝试读取 chunk header
|
||||
}
|
||||
|
||||
// 读取 chunk 数据
|
||||
@@ -216,6 +255,13 @@ func (r *ChunkReader) ReadMessage() (*RTMPMessage, error) {
|
||||
StreamID: state.header.MessageSID,
|
||||
Data: state.data,
|
||||
}
|
||||
|
||||
// 调试日志:记录消息完成信息
|
||||
if msg.TypeID == RTMP_MSG_AMF0_DATA || msg.TypeID == RTMP_MSG_AUDIO || msg.TypeID == RTMP_MSG_VIDEO {
|
||||
log.Printf("✅ [RTMP Debug] 消息完成: csid=%d type=%d len=%d",
|
||||
header.ChunkStreamID, msg.TypeID, len(msg.Data))
|
||||
}
|
||||
|
||||
// 清除消息缓冲
|
||||
delete(r.messageBuffer, header.ChunkStreamID)
|
||||
return msg, nil
|
||||
@@ -233,6 +279,12 @@ func (r *ChunkReader) readChunkHeader() (*ChunkHeader, error) {
|
||||
|
||||
format := (firstByte >> 6) & 0x03
|
||||
csid := uint32(firstByte & 0x3F)
|
||||
|
||||
// 调试日志:记录每个 chunk header 的原始字节
|
||||
// 注意:csid > 64 或异常 csid 值可能表示流同步丢失
|
||||
if csid > 20 || (format != 0 && format != 3) {
|
||||
log.Printf("🔍 [RTMP Debug] firstByte=0x%02X fmt=%d csid=%d", firstByte, format, csid)
|
||||
}
|
||||
|
||||
// 扩展 chunk stream ID
|
||||
if csid == 0 {
|
||||
@@ -282,6 +334,10 @@ func (r *ChunkReader) readChunkHeader() (*ChunkHeader, error) {
|
||||
header.MessageSID = binary.LittleEndian.Uint32(data[7:11])
|
||||
// 标记此 csid 已收到有效头部
|
||||
r.knownCSIDs[csid] = true
|
||||
|
||||
// 调试日志:记录 fmt=0 的完整信息
|
||||
log.Printf("🔍 [RTMP Debug] fmt=0 csid=%d ts=%d msgLen=%d typeID=%d streamID=%d",
|
||||
csid, header.Timestamp, header.MessageLength, header.MessageTypeID, header.MessageSID)
|
||||
|
||||
case CHUNK_FMT_1:
|
||||
// 7 bytes: timestamp delta(3) + length(3) + typeID(1)
|
||||
@@ -304,17 +360,30 @@ func (r *ChunkReader) readChunkHeader() (*ChunkHeader, error) {
|
||||
}
|
||||
delta := uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
|
||||
header.Timestamp = prevHeader.Timestamp + delta
|
||||
// 放宽验证:如果没有有效的 prevHeader,记录警告但不立即失败
|
||||
// 依赖后续 ReadMessage 中的 toRead==0 检查来捕获真正的无效数据
|
||||
|
||||
// 关键修复:如果没有有效的 prevHeader,尝试根据 csid 推断消息类型
|
||||
// 微信小程序 live-pusher 可能首次发送音视频时就使用 fmt=2
|
||||
if prevHeader.MessageLength == 0 && !r.knownCSIDs[csid] {
|
||||
log.Printf("⚠️ [RTMP Protocol] fmt=2 on csid %d without valid prevHeader, may cause issues", csid)
|
||||
// 尝试推断消息类型(基于常见的 RTMP 实现)
|
||||
// csid >= 4 通常用于音视频数据
|
||||
if csid >= 4 {
|
||||
// 设置默认消息长度为 chunkSize(会在后续 chunk 中累积)
|
||||
// 这是一个启发式处理,让数据能够被读取
|
||||
r.mu.RLock()
|
||||
header.MessageLength = r.chunkSize
|
||||
r.mu.RUnlock()
|
||||
// 猜测消息类型:偶数 csid 可能是音频,奇数是视频(这是一个启发式)
|
||||
// 实际上我们需要从数据本身来判断
|
||||
log.Printf("⚠️ [RTMP Protocol] fmt=2 csid=%d 无 prevHeader,设置临时 msgLen=%d",
|
||||
csid, header.MessageLength)
|
||||
} else {
|
||||
log.Printf("⚠️ [RTMP Protocol] fmt=2 on csid %d without valid prevHeader", csid)
|
||||
}
|
||||
}
|
||||
|
||||
case CHUNK_FMT_3:
|
||||
// 0 bytes: 使用上一个头部的所有字段(已经复制了 prevHeader 的值)
|
||||
// 放宽验证:如果没有有效的 prevHeader,记录警告但不立即失败
|
||||
// 某些 RTMP 客户端(如小程序 live-pusher)可能在首次使用某 csid 时就用 fmt=3
|
||||
// 检查是否在 messageBuffer 中有未完成的消息(消息续传场景)
|
||||
// 关键修复:检查消息续传或尝试推断
|
||||
if prevHeader.MessageLength == 0 && !r.knownCSIDs[csid] {
|
||||
// 检查是否是消息续传
|
||||
if state, exists := r.messageBuffer[csid]; exists && state.bytesRead < state.header.MessageLength {
|
||||
@@ -324,9 +393,15 @@ func (r *ChunkReader) readChunkHeader() (*ChunkHeader, error) {
|
||||
header.MessageSID = state.header.MessageSID
|
||||
header.Timestamp = state.header.Timestamp
|
||||
log.Printf("ℹ️ [RTMP Protocol] fmt=3 续传 csid=%d msgLen=%d", csid, header.MessageLength)
|
||||
} else if csid >= 4 {
|
||||
// 对于可能的音视频 csid,设置默认消息长度
|
||||
r.mu.RLock()
|
||||
header.MessageLength = r.chunkSize
|
||||
r.mu.RUnlock()
|
||||
log.Printf("⚠️ [RTMP Protocol] fmt=3 csid=%d 无 prevHeader,设置临时 msgLen=%d",
|
||||
csid, header.MessageLength)
|
||||
} else {
|
||||
log.Printf("⚠️ [RTMP Protocol] fmt=3 on csid %d without valid prevHeader (首次使用), msgLen=0", csid)
|
||||
// 不返回错误,让 ReadMessage 中的 toRead==0 检查来处理
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user