OBS-rtmp源码剖析之rtmp网络数据流读写操作(五)

这篇主要讲到了RTMP_Read()、RTMP_Write()、Read_1_Packet()和RTMP_SendPacket()函数
1、RTMP读取函数

static const char flvHeader[] = { 'F', 'L', 'V', 0x01,
                                  0x00,             /* 0x04 == audio, 0x01 == video */
                                  0x00, 0x00, 0x00, 0x09,
                                  0x00, 0x00, 0x00, 0x00
                                };

#define HEADERBUF   (128*1024)
int
RTMP_Read(RTMP *r, char *buf, int size)
{
    int nRead = 0, total = 0;

    /* can't continue */
fail:
    switch (r->m_read.status)
    {
    case RTMP_READ_EOF:
    case RTMP_READ_COMPLETE:
        return 0;
    case RTMP_READ_ERROR:  /* corrupted stream, resume failed */
        SetSockError(EINVAL);
        return -1;
    default:
        break;
    }

    /* first time thru */
    if (!(r->m_read.flags & RTMP_READ_HEADER))
    {
        if (!(r->m_read.flags & RTMP_READ_RESUME))
        {
            char *mybuf = malloc(HEADERBUF), *end = mybuf + HEADERBUF;
            int cnt = 0;
            r->m_read.buf = mybuf;
            r->m_read.buflen = HEADERBUF;

            memcpy(mybuf, flvHeader, sizeof(flvHeader));
            r->m_read.buf += sizeof(flvHeader);
            r->m_read.buflen -= sizeof(flvHeader);
            cnt += sizeof(flvHeader);

            while (r->m_read.timestamp == 0)
            {
                nRead = Read_1_Packet(r, r->m_read.buf, r->m_read.buflen);
                if (nRead < 0)
                {
                    free(mybuf);
                    r->m_read.buf = NULL;
                    r->m_read.buflen = 0;
                    r->m_read.status = nRead;
                    goto fail;
                }
                /* buffer overflow, fix buffer and give up */
                if (r->m_read.buf < mybuf || r->m_read.buf > end)
                {
                    mybuf = realloc(mybuf, cnt + nRead);
                    memcpy(mybuf+cnt, r->m_read.buf, nRead);
                    free(r->m_read.buf);
                    r->m_read.buf = mybuf+cnt+nRead;
                    break;
                }
                cnt += nRead;
                r->m_read.buf += nRead;
                r->m_read.buflen -= nRead;
                if (r->m_read.dataType == 5)
                    break;
            }
            mybuf[4] = r->m_read.dataType;
            r->m_read.buflen = r->m_read.buf - mybuf;
            r->m_read.buf = mybuf;
            r->m_read.bufpos = mybuf;
        }
        r->m_read.flags |= RTMP_READ_HEADER;
    }

    if ((r->m_read.flags & RTMP_READ_SEEKING) && r->m_read.buf)
    {
        /* drop whatever's here */
        free(r->m_read.buf);
        r->m_read.buf = NULL;
        r->m_read.bufpos = NULL;
        r->m_read.buflen = 0;
    }

    /* If there's leftover data buffered, use it up */
    if (r->m_read.buf)
    {
        nRead = r->m_read.buflen;
        if (nRead > size)
            nRead = size;
        memcpy(buf, r->m_read.bufpos, nRead);
        r->m_read.buflen -= nRead;
        if (!r->m_read.buflen)
        {
            free(r->m_read.buf);
            r->m_read.buf = NULL;
            r->m_read.bufpos = NULL;
        }
        else
        {
            r->m_read.bufpos += nRead;
        }
        buf += nRead;
        total += nRead;
        size -= nRead;
    }

    while (size > 0 && (nRead = Read_1_Packet(r, buf, size)) >= 0)
    {
        if (!nRead) continue;
        buf += nRead;
        total += nRead;
        size -= nRead;
        break;
    }
    if (nRead < 0)
        r->m_read.status = nRead;

    if (size < 0)
        total += size;
    return total;
}

2、RTMP发送函数

static const AVal av_setDataFrame = AVC("@setDataFrame");

int
RTMP_Write(RTMP *r, const char *buf, int size, int streamIdx)
{
    RTMPPacket *pkt = &r->m_write;
    char *pend, *enc;
    int s2 = size, ret, num;

    pkt->m_nChannel = 0x04;/* 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;
            }

            pkt->m_packetType = *buf++;
            pkt->m_nBodySize = AMF_DecodeInt24(buf);
            buf += 3;
            pkt->m_nTimeStamp = AMF_DecodeInt24(buf);
            buf += 3;
            pkt->m_nTimeStamp |= *buf++ << 24;
            buf += 3;
            s2 -= 11;

            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)
            {
                pkt->m_headerType = RTMP_PACKET_SIZE_LARGE;
                if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
                    pkt->m_nBodySize += 16;
            }
            else
            {
                pkt->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
            }

            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 = pkt->m_nBodySize - pkt->m_nBytesRead;
        if (num > s2)
            num = s2;
        memcpy(enc, buf, num);
        pkt->m_nBytesRead += num;
        s2 -= num;
        buf += num;
        if (pkt->m_nBytesRead == pkt->m_nBodySize)
        {
            ret = RTMP_SendPacket(r, pkt, FALSE);
            RTMPPacket_Free(pkt);
            pkt->m_nBytesRead = 0;
            if (!ret)
                return -1;
            buf += 4;
            s2 -= 4;
            if (s2 < 0)
                break;
        }
    }
    return size+s2;
}

3、读取一个rtmp包

/* Read from the stream until we get a media packet.
 * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
 * packets, 0 if ignorable error, >0 if there is a media packet
 */
static int
Read_1_Packet(RTMP *r, char *buf, unsigned int buflen)
{
    uint32_t prevTagSize = 0;
    int rtnGetNextMediaPacket = 0, ret = RTMP_READ_EOF;
    RTMPPacket packet = { 0 };
    int recopy = FALSE;
    unsigned int size;
    char *ptr, *pend;
    uint32_t nTimeStamp = 0;
    unsigned int len;

    rtnGetNextMediaPacket = RTMP_GetNextMediaPacket(r, &packet);
    while (rtnGetNextMediaPacket)
    {
        char *packetBody = packet.m_body;
        unsigned int nPacketLen = packet.m_nBodySize;

        /* Return RTMP_READ_COMPLETE if this was completed nicely with
         * invoke message Play.Stop or Play.Complete
         */
        if (rtnGetNextMediaPacket == 2)
        {
            RTMP_Log(RTMP_LOGDEBUG,
                     "Got Play.Complete or Play.Stop from server. "
                     "Assuming stream is complete");
            ret = RTMP_READ_COMPLETE;
            break;
        }

        r->m_read.dataType |= (((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) << 2) |
                               (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO));

        if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen <= 5)
        {
            RTMP_Log(RTMP_LOGDEBUG, "ignoring too small video packet: size: %d",
                     nPacketLen);
            ret = RTMP_READ_IGNORE;
            break;
        }
        if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO && nPacketLen <= 1)
        {
            RTMP_Log(RTMP_LOGDEBUG, "ignoring too small audio packet: size: %d",
                     nPacketLen);
            ret = RTMP_READ_IGNORE;
            break;
        }

        if (r->m_read.flags & RTMP_READ_SEEKING)
        {
            ret = RTMP_READ_IGNORE;
            break;
        }
#ifdef _DEBUG
        RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
                 packet.m_packetType, nPacketLen, packet.m_nTimeStamp,
                 packet.m_hasAbsTimestamp);
        if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO)
            RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0));
#endif

        if (r->m_read.flags & RTMP_READ_RESUME)
        {
            /* check the header if we get one */
            if (packet.m_nTimeStamp == 0)
            {
                if (r->m_read.nMetaHeaderSize > 0
                        && packet.m_packetType == RTMP_PACKET_TYPE_INFO)
                {
                    AMFObject metaObj;
                    int nRes =
                        AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE);
                    if (nRes >= 0)
                    {
                        AVal metastring;
                        AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0),
                                          &metastring);

                        if (AVMATCH(&metastring, &av_onMetaData))
                        {
                            /* compare */
                            if ((r->m_read.nMetaHeaderSize != nPacketLen) ||
                                    (memcmp
                                     (r->m_read.metaHeader, packetBody,
                                      r->m_read.nMetaHeaderSize) != 0))
                            {
                                ret = RTMP_READ_ERROR;
                            }
                        }
                        AMF_Reset(&metaObj);
                        if (ret == RTMP_READ_ERROR)
                            break;
                    }
                }

                /* check first keyframe to make sure we got the right position
                 * in the stream! (the first non ignored frame)
                 */
                if (r->m_read.nInitialFrameSize > 0)
                {
                    /* video or audio data */
                    if (packet.m_packetType == r->m_read.initialFrameType
                            && r->m_read.nInitialFrameSize == nPacketLen)
                    {
                        /* we don't compare the sizes since the packet can
                         * contain several FLV packets, just make sure the
                         * first frame is our keyframe (which we are going
                         * to rewrite)
                         */
                        if (memcmp
                                (r->m_read.initialFrame, packetBody,
                                 r->m_read.nInitialFrameSize) == 0)
                        {
                            RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!");
                            r->m_read.flags |= RTMP_READ_GOTKF;
                            /* ignore it! (what about audio data after it? it is
                             * handled by ignoring all 0ms frames, see below)
                             */
                            ret = RTMP_READ_IGNORE;
                            break;
                        }
                    }

                    /* hande FLV streams, even though the server resends the
                     * keyframe as an extra video packet it is also included
                     * in the first FLV stream chunk and we have to compare
                     * it and filter it out !!
                     */
                    if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
                    {
                        /* basically we have to find the keyframe with the
                         * correct TS being nResumeTS
                         */
                        unsigned int pos = 0;
                        uint32_t ts = 0;

                        while (pos + 11 < nPacketLen)
                        {
                            /* size without header (11) and prevTagSize (4) */
                            uint32_t dataSize =
                                AMF_DecodeInt24(packetBody + pos + 1);
                            ts = AMF_DecodeInt24(packetBody + pos + 4);
                            ts |= (packetBody[pos + 7] << 24);

#ifdef _DEBUG
                            RTMP_Log(RTMP_LOGDEBUG,
                                     "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
                                     packetBody[pos], dataSize, ts);
#endif
                            /* ok, is it a keyframe?:
                             * well doesn't work for audio!
                             */
                            if (packetBody[pos /*6928, test 0 */ ] ==
                                    r->m_read.initialFrameType
                                    /* && (packetBody[11]&0xf0) == 0x10 */ )
                            {
                                if (ts == r->m_read.nResumeTS)
                                {
                                    RTMP_Log(RTMP_LOGDEBUG,
                                             "Found keyframe with resume-keyframe timestamp!");
                                    if (r->m_read.nInitialFrameSize != dataSize
                                            || memcmp(r->m_read.initialFrame,
                                                      packetBody + pos + 11,
                                                      r->m_read.
                                                      nInitialFrameSize) != 0)
                                    {
                                        RTMP_Log(RTMP_LOGERROR,
                                                 "FLV Stream: Keyframe doesn't match!");
                                        ret = RTMP_READ_ERROR;
                                        break;
                                    }
                                    r->m_read.flags |= RTMP_READ_GOTFLVK;

                                    /* skip this packet?
                                     * check whether skippable:
                                     */
                                    if (pos + 11 + dataSize + 4 > nPacketLen)
                                    {
                                        RTMP_Log(RTMP_LOGWARNING,
                                                 "Non skipable packet since it doesn't end with chunk, stream corrupt!");
                                        ret = RTMP_READ_ERROR;
                                        break;
                                    }
                                    packetBody += (pos + 11 + dataSize + 4);
                                    nPacketLen -= (pos + 11 + dataSize + 4);

                                    goto stopKeyframeSearch;

                                }
                                else if (r->m_read.nResumeTS < ts)
                                {
                                    /* the timestamp ts will only increase with
                                     * further packets, wait for seek
                                     */
                                    goto stopKeyframeSearch;
                                }
                            }
                            pos += (11 + dataSize + 4);
                        }
                        if (ts < r->m_read.nResumeTS)
                        {
                            RTMP_Log(RTMP_LOGERROR,
                                     "First packet does not contain keyframe, all "
                                     "timestamps are smaller than the keyframe "
                                     "timestamp; probably the resume seek failed?");
                        }
stopKeyframeSearch:
                        ;
                        if (!(r->m_read.flags & RTMP_READ_GOTFLVK))
                        {
                            RTMP_Log(RTMP_LOGERROR,
                                     "Couldn't find the seeked keyframe in this chunk!");
                            ret = RTMP_READ_IGNORE;
                            break;
                        }
                    }
                }
            }

            if (packet.m_nTimeStamp > 0
                    && (r->m_read.flags & (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK)))
            {
                /* another problem is that the server can actually change from
                 * 09/08 video/audio packets to an FLV stream or vice versa and
                 * our keyframe check will prevent us from going along with the
                 * new stream if we resumed.
                 *
                 * in this case set the 'found keyframe' variables to true.
                 * We assume that if we found one keyframe somewhere and were
                 * already beyond TS > 0 we have written data to the output
                 * which means we can accept all forthcoming data including the
                 * change between 08/09 <-> FLV packets
                 */
                r->m_read.flags |= (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK);
            }

            /* skip till we find our keyframe
             * (seeking might put us somewhere before it)
             */
            if (!(r->m_read.flags & RTMP_READ_GOTKF) &&
                    packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
            {
                RTMP_Log(RTMP_LOGWARNING,
                         "Stream does not start with requested frame, ignoring data... ");
                r->m_read.nIgnoredFrameCounter++;
                if (r->m_read.nIgnoredFrameCounter > MAX_IGNORED_FRAMES)
                    ret = RTMP_READ_ERROR;  /* fatal error, couldn't continue stream */
                else
                    ret = RTMP_READ_IGNORE;
                break;
            }
            /* ok, do the same for FLV streams */
            if (!(r->m_read.flags & RTMP_READ_GOTFLVK) &&
                    packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
            {
                RTMP_Log(RTMP_LOGWARNING,
                         "Stream does not start with requested FLV frame, ignoring data... ");
                r->m_read.nIgnoredFlvFrameCounter++;
                if (r->m_read.nIgnoredFlvFrameCounter > MAX_IGNORED_FRAMES)
                    ret = RTMP_READ_ERROR;
                else
                    ret = RTMP_READ_IGNORE;
                break;
            }

            /* we have to ignore the 0ms frames since these are the first
             * keyframes; we've got these so don't mess around with multiple
             * copies sent by the server to us! (if the keyframe is found at a
             * later position there is only one copy and it will be ignored by
             * the preceding if clause)
             */
            if (!(r->m_read.flags & RTMP_READ_NO_IGNORE) &&
                    packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
            {
                /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
                 * contain several FLV packets
                 */
                if (packet.m_nTimeStamp == 0)
                {
                    ret = RTMP_READ_IGNORE;
                    break;
                }
                else
                {
                    /* stop ignoring packets */
                    r->m_read.flags |= RTMP_READ_NO_IGNORE;
                }
            }
        }

        /* calculate packet size and allocate slop buffer if necessary */
        size = nPacketLen +
               ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO
                 || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO
                 || packet.m_packetType == RTMP_PACKET_TYPE_INFO) ? 11 : 0) +
               (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO ? 4 : 0);

        if (size + 4 > buflen)
        {
            /* the extra 4 is for the case of an FLV stream without a last
             * prevTagSize (we need extra 4 bytes to append it) */
            r->m_read.buf = malloc(size + 4);
            if (r->m_read.buf == 0)
            {
                RTMP_Log(RTMP_LOGERROR, "Couldn't allocate memory!");
                ret = RTMP_READ_ERROR;      /* fatal error */
                break;
            }
            recopy = TRUE;
            ptr = r->m_read.buf;
        }
        else
        {
            ptr = buf;
        }
        pend = ptr + size + 4;

        /* use to return timestamp of last processed packet */

        /* audio (0x08), video (0x09) or metadata (0x12) packets :
         * construct 11 byte header then add rtmp packet's data */
        if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO
                || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO
                || packet.m_packetType == RTMP_PACKET_TYPE_INFO)
        {
            nTimeStamp = r->m_read.nResumeTS + packet.m_nTimeStamp;
            prevTagSize = 11 + nPacketLen;

            *ptr = packet.m_packetType;
            ptr++;
            ptr = AMF_EncodeInt24(ptr, pend, nPacketLen);

#if 0
            if(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO)
            {

                /* H264 fix: */
                if((packetBody[0] & 0x0f) == 7)   /* CodecId = H264 */
                {
                    uint8_t packetType = *(packetBody+1);

                    uint32_t ts = AMF_DecodeInt24(packetBody+2); /* composition time */
                    int32_t cts = (ts+0xff800000)^0xff800000;
                    RTMP_Log(RTMP_LOGDEBUG, "cts  : %d\n", cts);

                    nTimeStamp -= cts;
                    /* get rid of the composition time */
                    CRTMP::EncodeInt24(packetBody+2, 0);
                }
                RTMP_Log(RTMP_LOGDEBUG, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp, nTimeStamp);
            }
#endif

            ptr = AMF_EncodeInt24(ptr, pend, nTimeStamp);
            *ptr = (char)((nTimeStamp & 0xFF000000) >> 24);
            ptr++;

            /* stream id */
            ptr = AMF_EncodeInt24(ptr, pend, 0);
        }

        memcpy(ptr, packetBody, nPacketLen);
        len = nPacketLen;

        /* correct tagSize and obtain timestamp if we have an FLV stream */
        if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
        {
            unsigned int pos = 0;
            int delta;

            /* grab first timestamp and see if it needs fixing */
            nTimeStamp = AMF_DecodeInt24(packetBody + 4);
            nTimeStamp |= (packetBody[7] << 24);
            delta = packet.m_nTimeStamp - nTimeStamp + r->m_read.nResumeTS;

            while (pos + 11 < nPacketLen)
            {
                /* size without header (11) and without prevTagSize (4) */
                uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1);
                nTimeStamp = AMF_DecodeInt24(packetBody + pos + 4);
                nTimeStamp |= (packetBody[pos + 7] << 24);

                if (delta)
                {
                    nTimeStamp += delta;
                    AMF_EncodeInt24(ptr+pos+4, pend, nTimeStamp);
                    ptr[pos+7] = nTimeStamp>>24;
                }

                /* set data type */
                r->m_read.dataType |= (((*(packetBody + pos) == 0x08) << 2) |
                                       (*(packetBody + pos) == 0x09));

                if (pos + 11 + dataSize + 4 > nPacketLen)
                {
                    if (pos + 11 + dataSize > nPacketLen)
                    {
                        RTMP_Log(RTMP_LOGERROR,
                                 "Wrong data size (%u), stream corrupted, aborting!",
                                 dataSize);
                        ret = RTMP_READ_ERROR;
                        break;
                    }
                    RTMP_Log(RTMP_LOGWARNING, "No tagSize found, appending!");

                    /* we have to append a last tagSize! */
                    prevTagSize = dataSize + 11;
                    AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend,
                                    prevTagSize);
                    size += 4;
                    len += 4;
                }
                else
                {
                    prevTagSize =
                        AMF_DecodeInt32(packetBody + pos + 11 + dataSize);

#ifdef _DEBUG
                    RTMP_Log(RTMP_LOGDEBUG,
                             "FLV Packet: type %02X, dataSize: %u, tagSize: %u, timeStamp: %u ms",
                             (unsigned char)packetBody[pos], dataSize, prevTagSize,
                             nTimeStamp);
#endif

                    if (prevTagSize != (dataSize + 11))
                    {
#ifdef _DEBUG
                        RTMP_Log(RTMP_LOGWARNING,
                                 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
                                 dataSize + 11);
#endif

                        prevTagSize = dataSize + 11;
                        AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend,
                                        prevTagSize);
                    }
                }

                pos += prevTagSize + 4; /*(11+dataSize+4); */
            }
        }
        ptr += len;

        if (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
        {
            /* FLV tag packets contain their own prevTagSize */
            AMF_EncodeInt32(ptr, pend, prevTagSize);
        }

        /* In non-live this nTimeStamp can contain an absolute TS.
         * Update ext timestamp with this absolute offset in non-live mode
         * otherwise report the relative one
         */
        /* RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, pktTS: %dms, TS: %dms, bLiveStream: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, nTimeStamp, r->Link.lFlags & RTMP_LF_LIVE); */
        r->m_read.timestamp = (r->Link.lFlags & RTMP_LF_LIVE) ? packet.m_nTimeStamp : nTimeStamp;

        ret = size;
        break;
    }

    if (rtnGetNextMediaPacket)
        RTMPPacket_Free(&packet);

    if (recopy)
    {
        len = ret > (int)(buflen) ? buflen : (unsigned int)(ret);
        memcpy(buf, r->m_read.buf, len);
        r->m_read.bufpos = r->m_read.buf + len;
        r->m_read.buflen = ret - len;
    }
    return ret;
}

4、发送一个rtmp包

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;
    }

    nSize = packetSize[packet->m_headerType];
    hSize = nSize;
    cSize = 0;
    t = packet->m_nTimeStamp - last;

    if (packet->m_body)
    {
        header = packet->m_body - nSize;
        hend = packet->m_body;
    }
    else
    {
        header = hbuf + 6;
        hend = hbuf + sizeof(hbuf);
    }

    if (packet->m_nChannel > 319)
        cSize = 2;
    else if (packet->m_nChannel > 63)
        cSize = 1;
    if (cSize)
    {
        header -= cSize;
        hSize += cSize;
    }

    if (nSize > 1 && t >= 0xffffff)
    {
        header -= 4;
        hSize += 4;
    }

    hptr = header;
    c = packet->m_headerType << 6;
    switch (cSize)
    {
    case 0:
        c |= packet->m_nChannel;
        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;
    }

    if (nSize > 1)
    {
        hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t);
    }

    if (nSize > 4)
    {
        hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize);
        *hptr++ = packet->m_packetType;
    }

    if (nSize > 8)
        hptr += EncodeInt32LE(hptr, packet->m_nInfoField2);

    if (nSize > 1 && t >= 0xffffff)
        hptr = AMF_EncodeInt32(hptr, hend, t);

    nSize = packet->m_nBodySize;
    buffer = packet->m_body;
    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)
    {
        int chunks = (nSize+nChunkSize-1) / nChunkSize;
        if (chunks > 1)
        {
            tlen = chunks * (cSize + 1) + nSize + hSize;
            tbuf = malloc(tlen);
            if (!tbuf)
                return FALSE;
            toff = tbuf;
        }
    }
    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);
        if (tbuf)
        {
            memcpy(toff, header, nChunkSize + hSize);
            toff += nChunkSize + hSize;
        }
        else
        {
            wrote = WriteN(r, header, nChunkSize + hSize);
            if (!wrote)
                return FALSE;
        }
        nSize -= nChunkSize;
        buffer += nChunkSize;
        hSize = 0;

        if (nSize > 0)
        {
            header = buffer - 1;
            hSize = 1;
            if (cSize)
            {
                header -= cSize;
                hSize += cSize;
            }
            *header = (0xc0 | c);
            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;
}

猜你喜欢

转载自blog.csdn.net/tong5956/article/details/81482762