GOとC++コードの比較、GOは25%のコードが少ない
16407 ワード
C++プロジェクト:https://github.com/winlinvip/simple-rtmp-server
GOアイテム:https://github.com/winlinvip/go.rtmp https://github.com/winlinvip/go.srs
RTMP最下層ストリーム解析、chunkヘッド情報、GO:161行C++:211行GOはC++より50行少なく、主に配列操作、エラー情報、戻り値であり、これらの特徴はGOのコードを少なくする.論理的に全く同じで、C++からGO関数に翻訳されます.
C++コードは以下の通りです.
GOコードは以下の通りです.
デバッグしても悪くない.しかし、C++のSRSはstを使用しており、デバッグは面倒です.
GOアイテム:https://github.com/winlinvip/go.rtmp https://github.com/winlinvip/go.srs
RTMP最下層ストリーム解析、chunkヘッド情報、GO:161行C++:211行GOはC++より50行少なく、主に配列操作、エラー情報、戻り値であり、これらの特徴はGOのコードを少なくする.論理的に全く同じで、C++からGO関数に翻訳されます.
C++コードは以下の通りです.
int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size)
{
int ret = ERROR_SUCCESS;
/**
* we should not assert anything about fmt, for the first packet.
* (when first packet, the chunk->msg is NULL).
* the fmt maybe 0/1/2/3, the FMLE will send a 0xC4 for some audio packet.
* the previous packet is:
* 04 // fmt=0, cid=4
* 00 00 1a // timestamp=26
* 00 00 9d // payload_length=157
* 08 // message_type=8(audio)
* 01 00 00 00 // stream_id=1
* the current packet maybe:
* c4 // fmt=3, cid=4
* it's ok, for the packet is audio, and timestamp delta is 26.
* the current packet must be parsed as:
* fmt=0, cid=4
* timestamp=26+26=52
* payload_length=157
* message_type=8(audio)
* stream_id=1
* so we must update the timestamp even fmt=3 for first packet.
*/
// fresh packet used to update the timestamp even fmt=3 for first packet.
bool is_fresh_packet = !chunk->msg;
// but, we can ensure that when a chunk stream is fresh,
// the fmt must be 0, a new stream.
if (chunk->msg_count == 0 && fmt != RTMP_FMT_TYPE0) {
ret = ERROR_RTMP_CHUNK_START;
srs_error("chunk stream is fresh, "
"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) {
chunk->msg = new SrsCommonMessage();
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, 3, 0};
mh_size = mh_sizes[(int)fmt];
srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size);
int required_size = bh_size + mh_size;
if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) {
if (ret != ERROR_SOCKET_TIMEOUT) {
srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret);
}
return ret;
}
char* p = buffer->bytes() + bh_size;
// parse the message header.
// see also: ngx_rtmp_recv
if (fmt <= RTMP_FMT_TYPE2) {
char* pp = (char*)&chunk->header.timestamp_delta;
pp[2] = *p++;
pp[1] = *p++;
pp[0] = *p++;
pp[3] = 0;
// fmt: 0
// timestamp: 3 bytes
// If the timestamp is greater than or equal to 16777215
// (hexadecimal 0x00ffffff), this value MUST be 16777215, and the
// ‘extended timestamp header’ MUST be present. Otherwise, this value
// SHOULD be the entire timestamp.
//
// fmt: 1 or 2
// timestamp delta: 3 bytes
// If the delta is greater than or equal to 16777215 (hexadecimal
// 0x00ffffff), this value MUST be 16777215, and the ‘extended
// timestamp header’ MUST be present. Otherwise, this value SHOULD be
// the entire delta.
chunk->extended_timestamp = (chunk->header.timestamp_delta >= RTMP_EXTENDED_TIMESTAMP);
if (chunk->extended_timestamp) {
// 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.
//
// if extended timestamp, the timestamp must >= RTMP_EXTENDED_TIMESTAMP
// we set the timestamp to RTMP_EXTENDED_TIMESTAMP to identify we
// got an extended timestamp.
chunk->header.timestamp = RTMP_EXTENDED_TIMESTAMP;
} else {
if (fmt == RTMP_FMT_TYPE0) {
// 6.1.2.1. Type 0
// For a type-0 chunk, the absolute timestamp of the message is sent
// here.
chunk->header.timestamp = chunk->header.timestamp_delta;
} else {
// 6.1.2.2. Type 1
// 6.1.2.3. Type 2
// For a type-1 or type-2 chunk, the difference between the previous
// chunk's timestamp and the current chunk's timestamp is sent here.
chunk->header.timestamp += chunk->header.timestamp_delta;
}
}
if (fmt <= RTMP_FMT_TYPE1) {
pp = (char*)&chunk->header.payload_length;
pp[2] = *p++;
pp[1] = *p++;
pp[0] = *p++;
pp[3] = 0;
// if msg exists in cache, the size must not changed.
if (chunk->msg->size > 0 && chunk->msg->size != chunk->header.payload_length) {
ret = ERROR_RTMP_PACKET_SIZE;
srs_error("msg exists in chunk cache, "
"size=%d cannot change to %d, ret=%d",
chunk->msg->size, chunk->header.payload_length, ret);
return ret;
}
chunk->header.message_type = *p++;
if (fmt == RTMP_FMT_TYPE0) {
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=%"PRId64", 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=%"PRId64", 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=%"PRId64"",
fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp);
}
} else {
// update the timestamp even fmt=3 for first stream
if (is_fresh_packet && !chunk->extended_timestamp) {
chunk->header.timestamp += chunk->header.timestamp_delta;
}
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;
required_size = bh_size + mh_size;
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, required_size)) != ERROR_SUCCESS) {
if (ret != ERROR_SOCKET_TIMEOUT) {
srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret);
}
return ret;
}
// ffmpeg/librtmp may donot send this filed, need to detect the value.
// @see also: http://blog.csdn.net/win_lin/article/details/13363699
u_int32_t timestamp = 0x00;
char* pp = (char*)×tamp;
pp[3] = *p++;
pp[2] = *p++;
pp[1] = *p++;
pp[0] = *p++;
// compare to the chunk timestamp, which is set by chunk message header
// type 0,1 or 2.
u_int32_t chunk_timestamp = chunk->header.timestamp;
if (chunk_timestamp > RTMP_EXTENDED_TIMESTAMP && chunk_timestamp != timestamp) {
mh_size -= 4;
srs_verbose("ignore the 4bytes extended timestamp. mh_size=%d", mh_size);
} else {
chunk->header.timestamp = timestamp;
}
srs_verbose("header read ext_time completed. time=%"PRId64"", chunk->header.timestamp);
}
// valid message
if (chunk->header.payload_length < 0) {
ret = ERROR_RTMP_MSG_INVLIAD_SIZE;
srs_error("RTMP message size must not be negative. size=%d, ret=%d",
chunk->header.payload_length, ret);
return ret;
}
// copy header to msg
chunk->msg->header = chunk->header;
// increase the msg count, the chunk stream can accept fmt=1/2/3 message now.
chunk->msg_count++;
return ret;
}
GOコードは以下の通りです.
func (r *RtmpProtocol) read_message_header(chunk *RtmpChunkStream, format byte) (mh_size int, err error) {
/**
* we should not assert anything about fmt, for the first packet.
* (when first packet, the chunk->msg is NULL).
* the fmt maybe 0/1/2/3, the FMLE will send a 0xC4 for some audio packet.
* the previous packet is:
* 04 // fmt=0, cid=4
* 00 00 1a // timestamp=26
* 00 00 9d // payload_length=157
* 08 // message_type=8(audio)
* 01 00 00 00 // stream_id=1
* the current packet maybe:
* c4 // fmt=3, cid=4
* it's ok, for the packet is audio, and timestamp delta is 26.
* the current packet must be parsed as:
* fmt=0, cid=4
* timestamp=26+26=52
* payload_length=157
* message_type=8(audio)
* stream_id=1
* so we must update the timestamp even fmt=3 for first packet.
*/
// fresh packet used to update the timestamp even fmt=3 for first packet.
is_fresh_packet := false
if chunk.Msg == nil {
is_fresh_packet = true
}
// but, we can ensure that when a chunk stream is fresh,
// the fmt must be 0, a new stream.
if chunk.MsgCount == 0 && format != RTMP_FMT_TYPE0 {
err = RtmpError{code:ERROR_RTMP_CHUNK_START, desc:"protocol error, fmt of first chunk must be 0"}
return
}
// when exists cache msg, means got an partial message,
// the fmt must not be type0 which means new message.
if chunk.Msg != nil && format == RTMP_FMT_TYPE0 {
err = RtmpError{code:ERROR_RTMP_CHUNK_START, desc:"protocol error, unexpect start of new chunk"}
return
}
// create msg when new chunk stream start
if chunk.Msg == nil {
chunk.Msg = new(RtmpMessage)
}
// read message header from socket to buffer.
mh_sizes := []int{11, 7, 3, 0}
mh_size = mh_sizes[int(format)];
if err = r.buffer.ensure_buffer_bytes(mh_size); err != nil {
return
}
// parse the message header.
// see also: ngx_rtmp_recv
if format <= RTMP_FMT_TYPE2 {
chunk.Header.TimestampDelta = r.buffer.ReadUInt24()
// fmt: 0
// timestamp: 3 bytes
// If the timestamp is greater than or equal to 16777215
// (hexadecimal 0x00ffffff), this value MUST be 16777215, and the
// ‘extended timestamp header’ MUST be present. Otherwise, this value
// SHOULD be the entire timestamp.
//
// fmt: 1 or 2
// timestamp delta: 3 bytes
// If the delta is greater than or equal to 16777215 (hexadecimal
// 0x00ffffff), this value MUST be 16777215, and the ‘extended
// timestamp header’ MUST be present. Otherwise, this value SHOULD be
// the entire delta.
if chunk.ExtendedTimestamp = false; chunk.Header.TimestampDelta >= RTMP_EXTENDED_TIMESTAMP {
chunk.ExtendedTimestamp = true
}
if chunk.ExtendedTimestamp {
// 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.
//
// if extended timestamp, the timestamp must >= RTMP_EXTENDED_TIMESTAMP
// we set the timestamp to RTMP_EXTENDED_TIMESTAMP to identify we
// got an extended timestamp.
chunk.Header.Timestamp = RTMP_EXTENDED_TIMESTAMP
} else {
if format == RTMP_FMT_TYPE0 {
// 6.1.2.1. Type 0
// For a type-0 chunk, the absolute timestamp of the message is sent
// here.
chunk.Header.Timestamp = uint64(chunk.Header.TimestampDelta)
} else {
// 6.1.2.2. Type 1
// 6.1.2.3. Type 2
// For a type-1 or type-2 chunk, the difference between the previous
// chunk's timestamp and the current chunk's timestamp is sent here.
chunk.Header.Timestamp += uint64(chunk.Header.TimestampDelta)
}
}
if format <= RTMP_FMT_TYPE1 {
chunk.Header.PayloadLength = r.buffer.ReadUInt24()
// if msg exists in cache, the size must not changed.
if chunk.Msg.payload != nil && len(chunk.Msg.payload) != int(chunk.Header.PayloadLength) {
err = RtmpError{code:ERROR_RTMP_PACKET_SIZE, desc:"cached message size should never change"}
return
}
chunk.Header.MessageType = r.buffer.ReadByte()
if format == RTMP_FMT_TYPE0 {
chunk.Header.StreamId = r.buffer.ReadUInt32Le()
}
}
} else {
// update the timestamp even fmt=3 for first stream
if is_fresh_packet && !chunk.ExtendedTimestamp {
chunk.Header.Timestamp += uint64(chunk.Header.TimestampDelta)
}
}
if chunk.ExtendedTimestamp {
mh_size += 4
if err = r.buffer.ensure_buffer_bytes(4); err != nil {
return
}
// ffmpeg/librtmp may donot send this filed, need to detect the value.
// @see also: http://blog.csdn.net/win_lin/article/details/13363699
timestamp := r.buffer.ReadUInt32()
// compare to the chunk timestamp, which is set by chunk message header
// type 0,1 or 2.
if chunk.Header.Timestamp > RTMP_EXTENDED_TIMESTAMP && chunk.Header.Timestamp != uint64(timestamp) {
mh_size -= 4
r.buffer.Next(-4)
} else {
chunk.Header.Timestamp = uint64(timestamp)
}
}
// valid message
if int32(chunk.Header.PayloadLength) < 0 {
err = RtmpError{code:ERROR_RTMP_MSG_INVLIAD_SIZE, desc:"chunk packet should never be negative"}
return
}
// copy header to msg
copy := *chunk.Header
chunk.Msg.Header = ©
// increase the msg count, the chunk stream can accept fmt=1/2/3 message now.
chunk.MsgCount++
return
}
デバッグしても悪くない.しかし、C++のSRSはstを使用しており、デバッグは面倒です.