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++コードは以下の通りです.
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を使用しており、デバッグは面倒です.