ngx_int_t
ngx_rtmp_aggregate_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
                                   ngx_chain_t *in)
{
    uint32_t            base_time, timestamp, prev_size;
    size_t              len;
    ngx_int_t           first;
    u_char             *last;
    ngx_int_t           rc;
    ngx_buf_t          *b;
    ngx_chain_t        *cl, *next;
    ngx_rtmp_header_t   ch;

    ch = *h;

    first = 1;
    base_time = 0;

    while (in) {
        if (ngx_rtmp_fetch_uint8(&in, &ch.type) != NGX_OK) {
            return NGX_OK;
        }

        if (ngx_rtmp_fetch_uint32(&in, &ch.mlen, 3) != NGX_OK) {
            return NGX_ERROR;
        }

        if (ngx_rtmp_fetch_uint32(&in, &timestamp, 3) != NGX_OK) {
            return NGX_ERROR;
        }

        if (ngx_rtmp_fetch_uint8(&in, (uint8_t *) &timestamp + 3) != NGX_OK)
        {
            return NGX_ERROR;
        }

        if (ngx_rtmp_fetch_uint32(&in, &ch.msid, 3) != NGX_OK)
        {
            return NGX_ERROR;
        }

        if (first) {
            base_time = timestamp;
            first = 0;
        }

        ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
                       "RTMP aggregate %s (%d) len=%uD time=%uD (+%D) msid=%uD",
                       ngx_rtmp_message_type(ch.type),
                       (ngx_int_t) ch.type, ch.mlen, ch.timestamp,
                       timestamp - base_time, ch.msid);

        /* limit chain */

        len = 0;
        cl = in;
        while (cl) {
            b = cl->buf;
            len += (b->last - b->pos);
            if (len > ch.mlen) {
                break;
            }
            cl = cl->next;
        }

        if (cl == NULL) {
            ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
                          "RTMP error parsing aggregate");
            return NGX_ERROR;
        }

        next = cl->next;
        cl->next = NULL;
        b = cl->buf;
        last = b->last;
        b->last -= (len - ch.mlen);

        /* handle aggregated message */

        ch.timestamp = h->timestamp + timestamp - base_time;

        rc = ngx_rtmp_receive_message(s, &ch, in);

        /* restore chain before checking the result */

        in = cl;
        in->next = next;
        b->pos = b->last;
        b->last = last;

        if (rc != NGX_OK) {
            return rc;
        }

        /* read 32-bit previous tag size */

        if (ngx_rtmp_fetch_uint32(&in, &prev_size, 4) != NGX_OK) {
            return NGX_OK;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
                       "RTMP aggregate prev_size=%uD", prev_size);
    }

    return NGX_OK;
}
static void
ngx_live_relay_httpflv_recv_body(void *request, ngx_http_request_t *hcr)
{
    ngx_int_t                   n;
    ngx_rtmp_session_t         *s;
    ngx_chain_t                *cl, *l, *in;
    ngx_rtmp_header_t          *h;
    ngx_rtmp_stream_t          *st = NULL;

    s = request;

    n = ngx_http_client_read_body(hcr, &cl);

    if (n == 0 || n == NGX_ERROR) {
        s->finalize_reason = n == 0? NGX_LIVE_NORMAL_CLOSE:
                                     NGX_LIVE_FLV_RECV_ERR;
        ngx_log_error(NGX_LOG_INFO, s->log, ngx_errno,
                "http relay, recv body error");
        ngx_rtmp_finalize_session(s);
        return;
    }

    l = cl;
    for (;;) {
        if (l && l->buf->pos == l->buf->last) {
            l = l->next;
        }

        if (l == NULL) {
            return;
        }

        n = ngx_live_relay_httpflv_parse(s, l->buf);

        if (n == NGX_ERROR) {
            ngx_log_error(NGX_LOG_ERR, s->log, 0,
                    "http relay, parse flv frame failed in state %d",
                    s->flv_state);
            ngx_http_client_finalize_request(hcr, 1);

            return;
        }

        if (n == NGX_AGAIN) {
            continue;
        }

        /* NGX_OK */
        st = &s->in_streams[0];
        h = &st->hdr;
        in = st->in;

        if (ngx_rtmp_receive_message(s, h, in) != NGX_OK) {
            ngx_rtmp_finalize_session(s);
            return;
        }

        ngx_put_chainbufs(st->in);
        st->in = NULL;
    }
}