rtmp源码分析之RTMP_Write 和 RTMP_SendPacket

typedef struct RTMPPacket
	{
		uint8_t m_headerType; //  chunk type id (2bit)fmt 对应message head {0,3,7,11} + (6bit)chunk stream id  /*大部分情况是一个字节
		uint8_t m_packetType; // Message type ID(1-7协议控制;8,9音视频;10以后为AMF编码消息)
		uint8_t m_hasAbsTimestamp;	/* timestamp absolute or relative? */
		int m_nChannel; //chunk stream id(chunk basic header)字段
		uint32_t m_nTimeStamp;	/* timestamp */
		int32_t m_nInfoField2;	/* last 4 bytes in a long header */
		uint32_t m_nBodySize; //经过AMF编码组包后,message的大小 (如果是音视频数据 即FLV格式一个Tag中Tag Data 大小)
		uint32_t m_nBytesRead; // 需要发送一个Tag Data 的数据大小或者是已读取数据大小
		RTMPChunk *m_chunk;
		// body 指向数据的起始处。创建 body 空间时,也把 chunk header 的空间也创立了,且在 body 前面,故可以将指针指向 body 前面
		char* m_body; // Tag Data 数据  Tag data的起始指针因为会循环读取,给m_body所在内存赋值,直到一个Tag Data 读完
					//当chunk中的内容为命令时,m_body为chunk body(data)的起始指针
	} RTMPPacket;

int
RTMP_Write(RTMP* r, const char* buf, int size, int streamIdx, int channel)
{
	RTMPPacket* pkt = &r->m_write;
	char* pend, * enc;
	int s2 = size;
	int ret = 0;
	int num = 0;
	int bodySize = 0;
	int bytesRead = 0;
	pkt->m_nChannel = channel;	/* source channel */
	pkt->m_nInfoField2 = r->Link.streams[streamIdx].id;

	while (s2)
	{
		if (!pkt->m_nBytesRead)
		{
			if (size < 11)
			{
				/* FLV pkt too small */
				return 0;
			}

			if (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V')
			{
				buf += 13;
				s2 -= 13;
			}
			// 获取 tag 类型
			pkt->m_packetType = *buf++;
			// 获取 data size 
			pkt->m_nBodySize = AMF_DecodeInt24(buf);
			bodySize = pkt->m_nBodySize;
			buf += 3;
			// 获取时间戳
			pkt->m_nTimeStamp = AMF_DecodeInt24(buf);
			buf += 3;
			// 加上扩展时间戳
			pkt->m_nTimeStamp |= *buf++ << 24;
			// 跳过 streamID
			buf += 3;
			// 总长度跳过 11
			s2 -= 11; 

			// RTMP_PACKET_TYPE_INFO 代表 script
			if (((pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO
				|| pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) &&
				!pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO) 
			{
				// 设置 basic header  chunk type id = 0  chunk stream id = 0
				// cs id = 0 表示 2字节头
				pkt->m_headerType = RTMP_PACKET_SIZE_LARGE;
				if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
				{
					pkt->m_nBodySize += 16;
					bodySize += 16;
				}
			}
			else
			{
				pkt->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
			}

			// 分配 chunk 空间,既分配了 data 空间,也分配了 chunk header 最大的空间
			if (!RTMPPacket_Alloc(pkt, pkt->m_nBodySize))
			{
				RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__);
				return FALSE;
			}
			enc = pkt->m_body; // 指向数据
			pend = enc + pkt->m_nBodySize; // 指向数据结尾
			if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
			{
				enc = AMF_EncodeString(enc, pend, &av_setDataFrame);
				pkt->m_nBytesRead = enc - pkt->m_body;
			}
		}
		else
		{
			enc = pkt->m_body + pkt->m_nBytesRead;
		}
		// num 为还未发送的数据个数
		num = pkt->m_nBodySize - pkt->m_nBytesRead;
		if (num < 0)
		{
			RTMP_Log(RTMP_LOGERROR, "%s error %d,%d", __FUNCTION__, pkt->m_nBodySize, pkt->m_nBytesRead);
			return FALSE;
		}
		if (num > s2)
			num = s2;
		if (num < 0)
		{
			RTMP_Log(RTMP_LOGERROR, "%s error %d", __FUNCTION__, s2);
			return FALSE;
		}
		memcpy(enc, buf, num); // 将 buf 拷贝到 pkt->m_body
		pkt->m_nBytesRead += num;
		bytesRead += num;
		s2 -= num;
		buf += num;
		if (pkt->m_nBytesRead == pkt->m_nBodySize)
		{
			// 把 pkt 发送出去
			ret = RTMP_SendPacket(r, pkt, FALSE);
			RTMPPacket_Free(pkt);
			pkt->m_nBytesRead = 0;
			bytesRead = 0;
			if (!ret)
				return -1;
			// 跳过 previousTagSize 
			// 所以 previousTagSize 作用是什么?
			// 快速向前 seek 数据
			buf += 4;
			s2 -= 4;
			if (s2 < 0)
				break;
		}
	}
	return size + s2;
}

int
RTMP_SendPacket(RTMP* r, RTMPPacket* packet, int queue)
{
	const RTMPPacket* prevPacket;
	uint32_t last = 0;
	int nSize;
	int hSize, cSize;
	char* header, * hptr, * hend, hbuf[RTMP_MAX_HEADER_SIZE], c;
	uint32_t t;
	char* buffer, * tbuf = NULL, * toff = NULL;
	int nChunkSize;
	int tlen;

	if (packet->m_nChannel >= r->m_channelsAllocatedOut)
	{
		int n = packet->m_nChannel + 10;
		RTMPPacket** packets = realloc(r->m_vecChannelsOut, sizeof(RTMPPacket*) * n);
		if (!packets)
		{
			free(r->m_vecChannelsOut);
			r->m_vecChannelsOut = NULL;
			r->m_channelsAllocatedOut = 0;
			return FALSE;
		}
		r->m_vecChannelsOut = packets;
		memset(r->m_vecChannelsOut + r->m_channelsAllocatedOut, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedOut));
		r->m_channelsAllocatedOut = n;
	}

	// 获取该通道,上一次的数据
	prevPacket = r->m_vecChannelsOut[packet->m_nChannel];
	if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE)
	{
		/* compress a bit by using the prev packet's attributes */
		if (prevPacket->m_nBodySize == packet->m_nBodySize
			&& prevPacket->m_packetType == packet->m_packetType
			&& packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM)
			packet->m_headerType = RTMP_PACKET_SIZE_SMALL;

		if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp
			&& packet->m_headerType == RTMP_PACKET_SIZE_SMALL)
			packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM;
		last = prevPacket->m_nTimeStamp;
	}

	if (packet->m_headerType > 3)	/* sanity */
	{
		RTMP_Log(RTMP_LOGERROR, "sanity failed!! trying to send header of type: 0x%02x.",
			(unsigned char)packet->m_headerType);
		return FALSE;
	}

	// 块头初始大小 = 基本头(1字节) + 块消息头大小(11/7/3/0) = [12, 8, 4, 1]
	// 块基本头是1-3字节,因此用变量cSize来表示剩下的0-2字节
	// nSize 表示块头初始大小, hSize表示块头大小, cSize来表示剩下的0-2字节
	// hSize = nSize + cSize + 4
	nSize = packetSize[packet->m_headerType];
	hSize = nSize;
	cSize = 0;

	if (packet->m_nTimeStamp < last)
	{
		RTMP_Log(RTMP_LOGWARNING, "timestamp not increase [%u] [%u] type[%d]", packet->m_nTimeStamp, last, packet->m_packetType);
	}

	// 时间戳增量
	t = packet->m_nTimeStamp - last;
	if (packet->m_body) 
	{
		// 块头的首指针  向前平移了  基本头(1字节) + 块消息头大小(11/7/3/0)  字节
		header = packet->m_body - nSize;
		// 块头的尾指针
		hend = packet->m_body;
	}
	else
	{
		header = hbuf + 6;
		hend = hbuf + sizeof(hbuf);
	}

	// 块基本头是1-3字节,因此用变量cSize来表示剩下的0-2字节
	if (packet->m_nChannel > 319) // 块流id(cs id)大于319,则块基本头占3个字节
		cSize = 2;
	else if (packet->m_nChannel > 63) // 块流id(cs id)在64与319之间,则块基本头占2个字节
		cSize = 1;
	if (cSize)
	{
		// 向前平移了 块基本头是1-3字节,因此用变量cSize来表示剩下的0-2字节 个字节
		header -= cSize;
		hSize += cSize;
	}

	// 如果 t>0xffffff, 则需要使用 extended timestamp
	if (nSize > 1 && t >= 0xffffff)
	{
		// 向前平移了 extended timestamp
		header -= 4;
		hSize += 4;
	}

	// 确定好 Header 的位置后,就可以开始赋值了
	hptr = header;

	// 设置 basic header


	// 0 1 2 3 4 5 6 7
	//	+ -+-+-+-+-+-+-+-+
	//	| fmt | cs id |
	//	+-+-+-+-+-+-+-+-+
	//	一字节头

	//	0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
	//	+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| fmt | 0 | cs id - 64 |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	两字节头

	//	0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
	//	+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| fmt | 1 | cs id - 64 |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	三字节头

	// 把ChunkBasicHeader的Fmt类型左移6位
	// cSize 表示块基本头剩下的0-2字节 
	c = packet->m_headerType << 6;
	switch (cSize)
	{
	case 0:
		c |= packet->m_nChannel; // chunk stream ID
		break;
	case 1:
		break;
	case 2:
		c |= 1;
		break;
	}
	*hptr++ = c;
	if (cSize)
	{
		int tmp = packet->m_nChannel - 64;
		*hptr++ = tmp & 0xff;
		if (cSize == 2)
			*hptr++ = tmp >> 8;
	}

	// 设置 message header


	//消息类型 0, fmt = 0,字节长度 = 11:

	//	0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	//	+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| timestamp | message length |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| message length(cont) | message type id | msg stream id |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| message stream id(cont) |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	消息类型 1, fmt = 1,字节长度 = 7:

	//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	//	+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| timestamp delta | message length |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| message length(cont) | message type id |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	消息类型 2, fmt = 2,字节长度 = 3:

	//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
	//	+ -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	| timestamp delta |
	//	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	//	消息类型 3, fmt = 3,字节长度 = 0:

	//没有消息头,消息头长度是 0
	// nSize 块头初始大小 = 基本头(1字节) + 块消息头大小(11/7/3/0) = [12, 8, 4, 1]
	if (nSize > 1)
	{
		// 写入 timestamp 或 timestamp delta
		hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t);
	}

	if (nSize > 4)
	{
		// 写入 message length
		hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize);
		// 写入 message type id
		*hptr++ = packet->m_packetType;
	}

	if (nSize > 8)
		// 写入 message stream id  
		// 还原Chunk为Message的时候都是根据这个ID来辨识是否是同一个消息的Chunk的
		hptr += EncodeInt32LE(hptr, packet->m_nInfoField2);

	if (nSize > 1 && t >= 0xffffff)
		// 写入 extended timestamp
		hptr = AMF_EncodeInt32(hptr, hend, t);

	// 后面的 nSize 表示 data 的长度
	nSize = packet->m_nBodySize;
	// buffer 表示 data 的指针
	buffer = packet->m_body;
	// Chunk大小,默认是128字节
	nChunkSize = r->m_outChunkSize;

	RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, (int)r->m_sb.sb_socket,
		nSize);
	/* send all chunks in one HTTP request */
	if (r->Link.protocol & RTMP_FEATURE_HTTP)
	{
		// 需要将 data 切分成几次发送
		int chunks = (nSize + nChunkSize - 1) / nChunkSize;
		if (chunks > 1)
		{

			// 注意:ChunkBasicHeader的长度 = cSize + 1
			// 消息分n块后总的开销:
			// n个ChunkBasicHeader,1个ChunkMsgHeader,1个Message负载
			// 实际上只有第一个Chunk是完整的,剩下的只有ChunkBasicHeader
			// ? 感觉应该是 (chunks-1) * (cSize + 1) + nSize + hSize?
			tlen = chunks * (cSize + 1) + nSize + hSize; // tlen 正真发送需要的字节数
			tbuf = malloc(tlen);
			if (!tbuf)
				return FALSE;
			toff = tbuf;
		}
	}
	// chunk 大小 chunk header + data
	while (nSize + hSize)
	{
		int wrote;

		if (nSize < nChunkSize)
			nChunkSize = nSize;

		RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t*)header, hSize);
		RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t*)buffer, nChunkSize);
		// tbuf 不为 0,代表一个 chunkSize 发送不完全部数据
		if (tbuf)
		{
			memcpy(toff, header, nChunkSize + hSize);
			toff += nChunkSize + hSize;
		}
		else
		{
			// 一次发送完全部数据
			wrote = WriteN(r, header, nChunkSize + hSize);
			if (!wrote)
				return FALSE;
		}
		// nSize 为剩下的 data 长度
		nSize -= nChunkSize;
		// buffer 往后移动 chunksize 
		buffer += nChunkSize;
		// chunk header 设为 0
		hSize = 0;

		if (nSize > 0)
		{
			header = buffer - 1;

			// basic header 字节
			hSize = 1;
			if (cSize)
			{
				header -= cSize;
				hSize += cSize;
			}

			// c 为 basic header 第一个字节
			*header = (0xc0 | c); // 将 fmt 设置为 3,表示 message header 和 extended timestamp 都为空
			if (cSize)
			{
				int tmp = packet->m_nChannel - 64;
				header[1] = tmp & 0xff;
				if (cSize == 2)
					header[2] = tmp >> 8;
			}
		}
	}
	if (tbuf)
	{
		int wrote = WriteN(r, tbuf, toff - tbuf);
		free(tbuf);
		tbuf = NULL;
		if (!wrote)
			return FALSE;
	}

	/* we invoked a remote method */
	if (packet->m_packetType == RTMP_PACKET_TYPE_INVOKE)
	{
		AVal method;
		char* ptr;
		ptr = packet->m_body + 1;
		AMF_DecodeString(ptr, &method);
		RTMP_Log(RTMP_LOGDEBUG, "Invoking %s", method.av_val);
		/* keep it in call queue till result arrives */
		if (queue)
		{
			int txn;
			ptr += 3 + method.av_len;
			txn = (int)AMF_DecodeNumber(ptr);
			AV_queue(&r->m_methodCalls, &r->m_numCalls, &method, txn);
		}
	}

	if (!r->m_vecChannelsOut[packet->m_nChannel])
		r->m_vecChannelsOut[packet->m_nChannel] = malloc(sizeof(RTMPPacket));
	memcpy(r->m_vecChannelsOut[packet->m_nChannel], packet, sizeof(RTMPPacket));
	return TRUE;
}

参考文献

https://blog.csdn.net/qq_39683826/article/details/114656972

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值