RTMP协议中的Chunk Stream ID (CID)的作用
一、协议分层
RTMP包是以Message的结构封装的,结构如下所示:
1)Message Type ID在1-7的消息用于协议控制,这些消息一般是RTMP协议自身管理要使用的消息,用户一般情况下无需操作其中的数据。
Message Type ID为8,9的消息分别用于传输音频和视频数据。Message Type ID为15-20的消息用于发送AMF编码的命令,负责用户与服务器之间的交互,比如播放,暂停等等。
2)StreamID是音视频流的唯一ID, 一路流如果既有音频包又有视频包,那么这路流音频包的StreamID和他视频包的StreamID相同。
一个Message大小不一,音频视频的Message往往差异较大,为了充分利用网络,需要将一个大的Message中的Body部分拆分到一个或者多个Chunk中
Chunk结构:
在拆分到多个Chunk中的时候,第一个Chunk携带完整的Message Header信息
因为一个流当中可以传输多个Chunk,那么多个Chunk怎么标记同属于一个Message的呢?
是通过Chunk Stream ID区分的,同一个Chunk Stream ID必然属于同一个Message
SRS中的Chunk接收和拼接成Message的代码可以证明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg) { int ret = ERROR_SUCCESS; // chunk stream basic header. char fmt = 0; int cid = 0; if ((ret = read_basic_header(fmt, cid)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error( "read basic header failed. ret=%d" , ret); } return ret; } srs_verbose( "read basic header success. fmt=%d, cid=%d" , fmt, cid); // the cid must not negative. srs_assert(cid >= 0); // get the cached chunk stream. SrsChunkStream* chunk = NULL ; // use chunk stream cache to get the chunk info. // @see https://github.com/ossrs/srs/issues/249 if (cid < SRS_PERF_CHUNK_STREAM_CACHE) { // chunk stream cache hit. srs_verbose( "cs-cache hit, cid=%d" , cid); // already init, use it direclty chunk = cs_cache[cid]; srs_verbose( "cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%" PRId64 ", sid=%d)" , chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); } else { // chunk stream cache miss, use map. if (chunk_streams.find(cid) == chunk_streams.end()) { chunk = chunk_streams[cid] = new SrsChunkStream(cid); // set the perfer cid of chunk, // which will copy to the message received. chunk->header.perfer_cid = cid; srs_verbose( "cache new chunk stream: fmt=%d, cid=%d" , fmt, cid); } else { chunk = chunk_streams[cid]; srs_verbose( "cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%" PRId64 ", sid=%d)" , chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); } } |
红色的是以CID为key的一个Map,每次的第一个Chunk过来的时候,都缓存起来,下一个Chunk来了之后进行追加。
因为TCP的有序,所以同一个Message中不同的Chunk会先后抵达。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架