1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-12 11:21:52 +00:00

decode message header

This commit is contained in:
winlin 2013-10-19 12:12:52 +08:00
parent f730ccbf9f
commit 2625ecc151
3 changed files with 193 additions and 7 deletions

View file

@ -50,5 +50,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_SOCKET_WRITE 209
#define ERROR_RTMP_PLAIN_REQUIRED 300
#define ERROR_RTMP_CHUNK_START 301
#endif

View file

@ -28,6 +28,71 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core_socket.hpp>
#include <srs_core_buffer.hpp>
/**
* 6.1.2. Chunk Message Header
* There are four different formats for the chunk message header,
* selected by the "fmt" field in the chunk basic header.
*/
// 6.1.2.1. Type 0
// Chunks of Type 0 are 11 bytes long. This type MUST be used at the
// start of a chunk stream, and whenever the stream timestamp goes
// backward (e.g., because of a backward seek).
#define RTMP_FMT_TYPE0 0
// 6.1.2.2. Type 1
// Chunks of Type 1 are 7 bytes long. The message stream ID is not
// included; this chunk takes the same stream ID as the preceding chunk.
// Streams with variable-sized messages (for example, many video
// formats) SHOULD use this format for the first chunk of each new
// message after the first.
#define RTMP_FMT_TYPE1 1
// 6.1.2.3. Type 2
// Chunks of Type 2 are 3 bytes long. Neither the stream ID nor the
// message length is included; this chunk has the same stream ID and
// message length as the preceding chunk. Streams with constant-sized
// messages (for example, some audio and data formats) SHOULD use this
// format for the first chunk of each message after the first.
#define RTMP_FMT_TYPE2 2
// 6.1.2.4. Type 3
// Chunks of Type 3 have no header. Stream ID, message length and
// timestamp delta are not present; chunks of this type take values from
// the preceding chunk. When a single message is split into chunks, all
// chunks of a message except the first one, SHOULD use this type. Refer
// to example 2 in section 6.2.2. Stream consisting of messages of
// exactly the same size, stream ID and spacing in time SHOULD use this
// type for all chunks after chunk of Type 2. Refer to example 1 in
// section 6.2.1. If the delta between the first message and the second
// message is same as the time stamp of first message, then chunk of
// type 3 would immediately follow the chunk of type 0 as there is no
// need for a chunk of type 2 to register the delta. If Type 3 chunk
// follows a Type 0 chunk, then timestamp delta for this Type 3 chunk is
// the same as the timestamp of Type 0 chunk.
#define RTMP_FMT_TYPE3 3
/**
* 6. Chunking
* The chunk size is configurable. It can be set using a control
* message(Set Chunk Size) as described in section 7.1. The maximum
* chunk size can be 65536 bytes and minimum 128 bytes. Larger values
* reduce CPU usage, but also commit to larger writes that can delay
* other content on lower bandwidth connections. Smaller chunks are not
* good for high-bit rate streaming. Chunk size is maintained
* independently for each direction.
*/
#define RTMP_DEFAULT_CHUNK_SIZE 128
/**
* 6.1. Chunk Format
* Extended timestamp: 0 or 4 bytes
* This field MUST be sent when the normal timsestamp is set to
* 0xffffff, it MUST NOT be sent if the normal timestamp is set to
* anything else. So for values less than 0xffffff the normal
* timestamp field SHOULD be used in which case the extended timestamp
* MUST NOT be present. For values greater than or equal to 0xffffff
* the normal timestamp field MUST NOT be used and MUST be set to
* 0xffffff and the extended timestamp MUST be sent.
*/
#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF
SrsProtocol::SrsProtocol(st_netfd_t client_stfd)
{
stfd = client_stfd;
@ -68,12 +133,12 @@ int SrsProtocol::recv_message(SrsMessage** pmsg)
// chunk stream basic header.
char fmt = 0;
int cid = 0;
int size = 0;
if ((ret = read_basic_header(fmt, cid, size)) != ERROR_SUCCESS) {
int bh_size = 0;
if ((ret = read_basic_header(fmt, cid, bh_size)) != ERROR_SUCCESS) {
srs_error("read basic header failed. ret=%d", ret);
return ret;
}
srs_info("read basic header success. fmt=%d, cid=%d, size=%d", fmt, cid, size);
srs_info("read basic header success. fmt=%d, cid=%d, bh_size=%d", fmt, cid, bh_size);
// get the cached chunk stream.
SrsChunkStream* chunk = NULL;
@ -89,11 +154,24 @@ int SrsProtocol::recv_message(SrsMessage** pmsg)
}
// chunk stream message header
SrsMessage* msg = NULL;
if ((ret = read_message_header(chunk, fmt, &msg)) != ERROR_SUCCESS) {
int mh_size = 0;
if ((ret = read_message_header(chunk, fmt, bh_size, mh_size)) != ERROR_SUCCESS) {
srs_error("read message header failed. ret=%d", ret);
return ret;
}
srs_info("read message header success. "
"fmt=%d, mh_size=%d, ext_time=%d, message(type=%d, size=%d, time=%d, sid=%d)",
fmt, mh_size, chunk->extended_timestamp, chunk->header.message_type,
chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id);
// read msg payload from chunk stream.
SrsMessage* msg = NULL;
/*int payload_size = 0;
if ((ret = read_message_payload(chunk, bh_size, mh_size, payload_size, &msg)) != ERROR_SUCCESS) {
srs_error("read message payload failed. ret=%d", ret);
return ret;
}
srs_info("read message payload success. payload_size=%d", payload_size);*/
// not got an entire RTMP message, try next chunk.
if (!msg) {
@ -111,6 +189,7 @@ int SrsProtocol::read_basic_header(char& fmt, int& cid, int& size)
int ret = ERROR_SUCCESS;
if ((ret = buffer->ensure_buffer_bytes(skt, 1)) != ERROR_SUCCESS) {
srs_error("read 1bytes basic header failed. ret=%d", ret);
return ret;
}
@ -121,19 +200,23 @@ int SrsProtocol::read_basic_header(char& fmt, int& cid, int& size)
size = 1;
if (cid > 1) {
srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", size, fmt, cid);
return ret;
}
if (cid == 0) {
if ((ret = buffer->ensure_buffer_bytes(skt, 2)) != ERROR_SUCCESS) {
srs_error("read 2bytes basic header failed. ret=%d", ret);
return ret;
}
cid = 64;
cid += *(++p);
size = 2;
srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", size, fmt, cid);
} else if (cid == 1) {
if ((ret = buffer->ensure_buffer_bytes(skt, 3)) != ERROR_SUCCESS) {
srs_error("read 3bytes basic header failed. ret=%d", ret);
return ret;
}
@ -141,6 +224,7 @@ int SrsProtocol::read_basic_header(char& fmt, int& cid, int& size)
cid += *(++p);
cid += *(++p) * 256;
size = 3;
srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", size, fmt, cid);
} else {
srs_error("invalid path, impossible basic header.");
srs_assert(false);
@ -149,9 +233,105 @@ int SrsProtocol::read_basic_header(char& fmt, int& cid, int& size)
return ret;
}
int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, SrsMessage** pmsg)
int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size)
{
int ret = ERROR_SUCCESS;
// when not exists cached msg, means get an new message,
// the fmt must be type0 which means new message.
if (!chunk->msg && fmt != RTMP_FMT_TYPE0) {
ret = ERROR_RTMP_CHUNK_START;
srs_error("chunk stream start, "
"fmt must be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret);
return ret;
}
// when exists cache msg, means got an partial message,
// the fmt must not be type0 which means new message.
if (chunk->msg && fmt == RTMP_FMT_TYPE0) {
ret = ERROR_RTMP_CHUNK_START;
srs_error("chunk stream exists, "
"fmt must not be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret);
return ret;
}
// create msg when new chunk stream start
if (!chunk->msg) {
srs_assert(fmt == RTMP_FMT_TYPE0);
chunk->msg = new SrsMessage();
srs_verbose("create message for new chunk, fmt=%d, cid=%d", fmt, chunk->cid);
}
// read message header from socket to buffer.
static char mh_sizes[] = {11, 7, 1, 0};
mh_size = mh_sizes[(int)fmt];
srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size);
if ((ret = buffer->ensure_buffer_bytes(skt, bh_size + mh_size)) != ERROR_SUCCESS) {
srs_error("read %dbytes message header failed. ret=%d", mh_size, ret);
return ret;
}
char* p = buffer->bytes() + bh_size;
// parse the message header.
// see also: ngx_rtmp_recv
if (fmt <= 2) {
char* pp = (char*)&chunk->header.timestamp;
pp[2] = *p++;
pp[1] = *p++;
pp[0] = *p++;
pp[3] = 0;
chunk->extended_timestamp = (chunk->header.timestamp == RTMP_EXTENDED_TIMESTAMP);
if (fmt <= 1) {
pp = (char*)&chunk->header.payload_length;
pp[2] = *p++;
pp[1] = *p++;
pp[0] = *p++;
pp[3] = 0;
chunk->header.message_type = *p++;
if (fmt == 0) {
pp = (char*)&chunk->header.stream_id;
pp[0] = *p++;
pp[1] = *p++;
pp[2] = *p++;
pp[3] = *p++;
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d, sid=%d",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length,
chunk->header.message_type, chunk->header.stream_id);
} else {
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length,
chunk->header.message_type);
}
} else {
srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp);
}
} else {
srs_verbose("header read completed. fmt=%d, size=%d, ext_time=%d",
fmt, mh_size, chunk->extended_timestamp);
}
if (chunk->extended_timestamp) {
mh_size += 4;
srs_verbose("read header ext time. fmt=%d, ext_time=%d, mh_size=%d", fmt, chunk->extended_timestamp, mh_size);
if ((ret = buffer->ensure_buffer_bytes(skt, bh_size + mh_size)) != ERROR_SUCCESS) {
srs_error("read %dbytes message header failed. ret=%d", mh_size, ret);
return ret;
}
char* pp = (char*)&chunk->header.timestamp;
pp[3] = *p++;
pp[2] = *p++;
pp[1] = *p++;
pp[0] = *p++;
srs_verbose("header read ext_time completed. time=%d", chunk->header.timestamp);
}
return ret;
}
@ -171,6 +351,7 @@ SrsChunkStream::SrsChunkStream(int _cid)
{
fmt = 0;
cid = _cid;
extended_timestamp = false;
msg = NULL;
}

View file

@ -58,7 +58,7 @@ public:
virtual int recv_message(SrsMessage** pmsg);
private:
virtual int read_basic_header(char& fmt, int& cid, int& size);
virtual int read_message_header(SrsChunkStream* chunk, char fmt, SrsMessage** pmsg);
virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size);
};
/**
@ -113,6 +113,10 @@ public:
*/
SrsMessageHeader header;
/**
* whether the chunk message header has extended timestamp.
*/
bool extended_timestamp;
/**
* partially read message.
*/
SrsMessage* msg;