int SrsRtmpConn::playing(SrsSource* source)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {
        srs_error("check play_refer failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("check play_refer success.");
    
    SrsConsumer* consumer = NULL;
    if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {
        srs_error("create consumer failed. ret=%d", ret);
        return ret;
    }
    
    srs_assert(consumer != NULL);
    SrsAutoFree(SrsConsumer, consumer, false);
    srs_verbose("consumer created success.");
    
    rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_US);
    
    SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER);

    int64_t starttime = -1;
    while (true) {
        // switch to other st-threads.
        st_usleep(0);
        
        pithy_print.elapse();

        // read from client.
        int ctl_msg_ret = ERROR_SUCCESS;
        if (true) {
            SrsMessage* msg = NULL;
            ctl_msg_ret = ret = rtmp->recv_message(&msg);
            
            srs_verbose("play loop recv message. ret=%d", ret);
            if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
                if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                    srs_error("recv client control message failed. ret=%d", ret);
                }
                return ret;
            }
            if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) {
                if (!srs_is_system_control_error(ret)) {
                    srs_error("process play control message failed. ret=%d", ret);
                }
                return ret;
            }
        }
        
        // get messages from consumer.
        SrsSharedPtrMessage** msgs = NULL;
        int count = 0;
        if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) {
            srs_error("get messages from consumer failed. ret=%d", ret);
            return ret;
        }

        // reportable
        if (pithy_print.can_print()) {
            srs_trace("-> "SRS_LOG_ID_PLAY
                " time=%"PRId64", duration=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", 
                pithy_print.age(), duration, ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), 
                rtmp->get_send_kbps(), rtmp->get_recv_kbps());
        }
        
        if (count <= 0) {
            srs_verbose("no packets in queue.");
            continue;
        }
        SrsAutoFree(SrsSharedPtrMessage*, msgs, true);
        
        // sendout messages
        for (int i = 0; i < count; i++) {
            SrsSharedPtrMessage* msg = msgs[i];
            
            // the send_message will free the msg, 
            // so set the msgs[i] to NULL.
            msgs[i] = NULL;
            
            srs_assert(msg);
            
            // foreach msg, collect the duration.
            // @remark: never use msg when sent it, for the protocol sdk will free it.
            if (starttime < 0 || starttime > msg->header.timestamp) {
                starttime = msg->header.timestamp;
            }
            duration += msg->header.timestamp - starttime;
            starttime = msg->header.timestamp;
            
            if ((ret = rtmp->send_and_free_message(msg)) != ERROR_SUCCESS) {
                srs_error("send message to client failed. ret=%d", ret);
                return ret;
            }
        }
        
        // if duration specified, and exceed it, stop play live.
        // @see: https://github.com/winlinvip/simple-rtmp-server/issues/45
        if (req->duration > 0 && duration >= (int64_t)req->duration) {
            ret = ERROR_RTMP_DURATION_EXCEED;
            srs_trace("stop live for duration exceed. ret=%d", ret);
            return ret;
        }
    }
    
    return ret;
}
int SrsRtmpConn::playing(SrsSource* source)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_play(req->vhost))) != ERROR_SUCCESS) {
        srs_error("check play_refer failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("check play_refer success.");
    
    SrsConsumer* consumer = NULL;
    if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) {
        srs_error("create consumer failed. ret=%d", ret);
        return ret;
    }
    
    srs_assert(consumer != NULL);
    SrsAutoFree(SrsConsumer, consumer, false);
    srs_verbose("consumer created success.");
    
    rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_US);
    
    SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER);

    while (true) {
        pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000);
        
        // switch to other st-threads.
        st_usleep(0);

        // read from client.
        int ctl_msg_ret = ERROR_SUCCESS;
        if (true) {
            SrsCommonMessage* msg = NULL;
            ctl_msg_ret = ret = rtmp->recv_message(&msg);
            
            srs_verbose("play loop recv message. ret=%d", ret);
            if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
                if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                    srs_error("recv client control message failed. ret=%d", ret);
                }
                return ret;
            }
            if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) {
                if (!srs_is_system_control_error(ret)) {
                    srs_error("process play control message failed. ret=%d", ret);
                }
                return ret;
            }
        }
        
        // get messages from consumer.
        SrsSharedPtrMessage** msgs = NULL;
        int count = 0;
        if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) {
            srs_error("get messages from consumer failed. ret=%d", ret);
            return ret;
        }

        // reportable
        if (pithy_print.can_print()) {
            srs_trace("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", 
                pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());
        }
        
        if (count <= 0) {
            srs_verbose("no packets in queue.");
            continue;
        }
        SrsAutoFree(SrsSharedPtrMessage*, msgs, true);
        
        // sendout messages
        for (int i = 0; i < count; i++) {
            SrsSharedPtrMessage* msg = msgs[i];
            
            // the send_message will free the msg, 
            // so set the msgs[i] to NULL.
            msgs[i] = NULL;
            
            if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
                srs_error("send message to client failed. ret=%d", ret);
                return ret;
            }
        }
    }
    
    return ret;
}