int MRtmpConnection::onCommand(MRtmpMessage *msg, const MString &name, double transactionID, MAMF0Any *arg1
                               , MAMF0Any *arg2, MAMF0Any *arg3, MAMF0Any *arg4)
{
    // TODO refer check.
    // TODO set app msg to protocol
    int ret = E_SUCCESS;

    if (name == "connect") {
        if (!arg1->isAmf0Object()) return E_AMF_TYPE_ERROR;

        MAMF0Object *obj = dynamic_cast<MAMF0Object *>(arg1);
        if ((ret = parseUrl(obj)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->setAckSize(2500000)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->setPeerBandwidth(2500000)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->onConnect(transactionID)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->onBWDone()) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "releaseStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, new MAMF0Undefined)) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "FCPublish") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, new MAMF0Undefined)) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "createStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID)
                                       , new MAMF0Null, new MAMF0Number(1))) != E_SUCCESS)
        {
            return ret;
        }
        m_protocol->getRtmpCtx()->streamID = 1;
    } else if (name == "publish") {
        MString cmdName = "FCPublish";
        MAMF0Object *obj = new MAMF0Object;
        obj->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Publish_Start));
        obj->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Publish_Start));

        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverStream;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj)) != E_SUCCESS) {
            return ret;
        }

        cmdName = RTMP_AMF0_COMMAND_ON_STATUS;
        MAMF0Object *obj1 = new MAMF0Object;
        obj1->setValue(STATUS_LEVEL, new MAMF0ShortString(STATUS_LEVEL_STATUS));
        obj1->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Publish_Start));
        obj1->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Publish_Start));
        obj1->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj1)) != E_SUCCESS) {
            return ret;
        }

        MAMF0ShortString *str = dynamic_cast<MAMF0ShortString *>(arg2);
        if (!str) {
            return E_AMF_TYPE_ERROR;
        }
        MRtmpContext *ctx = m_protocol->getRtmpCtx();
        ctx->setStreamName(str->var);

        MString url = ctx->rtmpUrl->url();
        m_source = new MRtmpSource(url, this);
        log_trace("start publish %s", url.c_str());

    } else if (name == "FCUnpublish") {
        MString cmdName = "onFCUnpublish";
        MAMF0Object *obj = new MAMF0Object;
        obj->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Unpublish_Success));
        obj->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Unpublish_Success));

        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj)) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "closeStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID)
                                       , new MAMF0Null, new MAMF0Undefined)) != E_SUCCESS)
        {
            return ret;
        }

        cmdName = RTMP_AMF0_COMMAND_ON_STATUS;
        MAMF0Object *obj1 = new MAMF0Object;
        obj1->setValue(STATUS_LEVEL, new MAMF0ShortString(STATUS_LEVEL_STATUS));
        obj1->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Unpublish_Success));
        obj1->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Unpublish_Success));
        obj1->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj1)) != E_SUCCESS) {
            return ret;
        }
        m_protocol->getRtmpCtx()->streamID = 0;
    } else if (name == "deleteStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection;
        header.type = RTMP_MSG_AMF0CommandMessage;

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID)
                                       , new MAMF0Null, new MAMF0Null)) != E_SUCCESS)
        {
            return ret;
        }
    } else if (name == "play") {
        if ((ret = m_protocol->setChunkSize(4096)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->setUCM(UCM_StreamBegin, m_protocol->getRtmpCtx()->streamID)) != E_SUCCESS) {
            return ret;
        }

        MRtmpMessageHeader header;
        header.perfer_cid = RTMP_CID_OverConnection2;
        header.type = RTMP_MSG_AMF0CommandMessage;

        MString cmdName = RTMP_AMF0_COMMAND_ON_STATUS;

        MAMF0Object *obj = new MAMF0Object;
        obj->setValue(STATUS_LEVEL, new MAMF0ShortString(STATUS_LEVEL_STATUS));
        obj->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Play_Reset));
        obj->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Play_Reset));
        obj->setValue(STATUS_DETAILS, new MAMF0ShortString(NetStream_Play_Reset));
        obj->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj)) != E_SUCCESS) {
            return ret;
        }

        MAMF0Object *obj1 = new MAMF0Object;
        obj1->setValue(STATUS_LEVEL, new MAMF0ShortString(STATUS_LEVEL_STATUS));
        obj1->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Play_Start));
        obj1->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Play_Start));
        obj1->setValue(STATUS_DETAILS, new MAMF0ShortString(NetStream_Play_Start));
        obj1->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj1)) != E_SUCCESS) {
            return ret;
        }

        header.type = RTMP_MSG_AMF0DataMessage;
        cmdName = RTMP_AMF0_DATA_SAMPLE_ACCESS;
        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Boolean(false), new MAMF0Boolean(false))) != E_SUCCESS) {
            return ret;
        }

        MAMF0ShortString *str = dynamic_cast<MAMF0ShortString *>(arg2);
        if (!str) {
            return E_AMF_TYPE_ERROR;
        }

        MRtmpContext *ctx = m_protocol->getRtmpCtx();
        ctx->setStreamName(str->var);
        MString url = ctx->rtmpUrl->url();
        while (true) {
            MRtmpSource *source = MRtmpSource::findSource(url);
            if (!source) {
                mSleep(1);
                continue;
            }

            m_source = source;
            break;
        }

        log_trace("start play : %s", url.c_str());
        playService();
    } else if (name == "_checkbw") {
        return ret;
    }

    else {
        log_error("MRtmpConnection onCommand : no method \"%s\"", name.c_str());
        return E_INVOKE_NO_METHOD;
    }

    return ret;
}
int MRtmpConnection::onCommand(MRtmpMessage *msg, const MString &name, double transactionID, MAMF0Any *arg1
                               , MAMF0Any *arg2, MAMF0Any *arg3, MAMF0Any *arg4)
{
    // TODO refer check.
    // TODO set app msg to protocol
    int ret = E_SUCCESS;

    if (name == "connect") {
        if (!arg1->isAmf0Object()) return E_AMF_TYPE_ERROR;

        MAMF0Object *obj = dynamic_cast<MAMF0Object *>(arg1);
        if ((ret = parseUrl(obj)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->setAckSize(2500000)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->setPeerBandwidth(2500000)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->onConnect(transactionID)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->onBWDone()) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "releaseStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection);

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, new MAMF0Undefined)) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "FCPublish") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection);

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, new MAMF0Undefined)) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "createStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection);

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID)
                                       , new MAMF0Null, new MAMF0Number(1))) != E_SUCCESS)
        {
            return ret;
        }
        m_protocol->getRtmpCtx()->streamID = 1;
    } else if (name == "publish") {
        MString cmdName = "FCPublish";
        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverStream);

        MRtmpNetStatusEvent *obj = new MRtmpNetStatusEvent(NetStream_Publish_Start);
        obj->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Publish_Start));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj)) != E_SUCCESS) {
            return ret;
        }

        MRtmpNetStatusEvent *obj1 = new MRtmpNetStatusEvent(NetStream_Publish_Start, STATUS_LEVEL_STATUS);
        obj1->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Publish_Start));
        obj1->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendNetStatusEvent(transactionID, obj1)) != E_SUCCESS) {
            return ret;
        }

        MAMF0ShortString *str = dynamic_cast<MAMF0ShortString *>(arg2);
        if (!str) {
            return E_AMF_TYPE_ERROR;
        }

        MRtmpContext *ctx = m_protocol->getRtmpCtx();
        ctx->setStreamName(str->var);

        MString url = ctx->rtmpUrl->url();
        m_source = MRtmpSource::findSource(url);
        m_role = Role_Connection_Publish;

        log_trace("start publish %s", url.c_str());

        g_cchannel->sendLine(Internal_CMD_IHasBackSourced, url);
        BlsBackSource::instance()->setHasBackSource(url);

    } else if (name == "FCUnpublish") {
        MString cmdName = "onFCUnpublish";
        MAMF0Object *obj = new MAMF0Object;
        obj->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Unpublish_Success));
        obj->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Unpublish_Success));

        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection);

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj)) != E_SUCCESS) {
            return ret;
        }
    } else if (name == "closeStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection);

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID)
                                       , new MAMF0Null, new MAMF0Undefined)) != E_SUCCESS)
        {
            return ret;
        }

        cmdName = RTMP_AMF0_COMMAND_ON_STATUS;
        MAMF0Object *obj1 = new MAMF0Object;
        obj1->setValue(STATUS_LEVEL, new MAMF0ShortString(STATUS_LEVEL_STATUS));
        obj1->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Unpublish_Success));
        obj1->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Unpublish_Success));
        obj1->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj1)) != E_SUCCESS) {
            return ret;
        }
        m_protocol->getRtmpCtx()->streamID = 0;
    } else if (name == "deleteStream") {
        MString cmdName = RTMP_AMF0_COMMAND_RESULT;
        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection);

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID)
                                       , new MAMF0Null, new MAMF0Null)) != E_SUCCESS)
        {
            return ret;
        }
    } else if (name == "play") {
        if ((ret = m_protocol->setChunkSize(40960)) != E_SUCCESS) {
            return ret;
        }

        if ((ret = m_protocol->setUCM(UCM_StreamBegin, m_protocol->getRtmpCtx()->streamID)) != E_SUCCESS) {
            return ret;
        }

        MRtmpMessageHeader header(RTMP_MSG_AMF0CommandMessage, RTMP_CID_OverConnection2);
        MString cmdName = RTMP_AMF0_COMMAND_ON_STATUS;

        MRtmpNetStatusEvent *obj = new MRtmpNetStatusEvent(NetStream_Play_Reset, STATUS_LEVEL_STATUS);
        obj->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Play_Reset));
        obj->setValue(STATUS_DETAILS, new MAMF0ShortString(NetStream_Play_Reset));
        obj->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj)) != E_SUCCESS) {
            return ret;
        }

        MAMF0Object *obj1 = new MAMF0Object;
        obj1->setValue(STATUS_LEVEL, new MAMF0ShortString(STATUS_LEVEL_STATUS));
        obj1->setValue(STATUS_CODE, new MAMF0ShortString(NetStream_Play_Start));
        obj1->setValue(STATUS_DESC, new MAMF0ShortString(NetStream_Play_Start));
        obj1->setValue(STATUS_DETAILS, new MAMF0ShortString(NetStream_Play_Start));
        obj1->setValue(STATUS_CLIENT_ID, new MAMF0ShortString("ASAICiss"));

        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Number(transactionID), new MAMF0Null, obj1)) != E_SUCCESS) {
            return ret;
        }

        header.type = RTMP_MSG_AMF0DataMessage;
        cmdName = RTMP_AMF0_DATA_SAMPLE_ACCESS;
        if ((ret = m_protocol->sendAny(header, new MAMF0ShortString(cmdName), new MAMF0Boolean(false), new MAMF0Boolean(false))) != E_SUCCESS) {
            return ret;
        }

        MAMF0ShortString *str = dynamic_cast<MAMF0ShortString *>(arg2);
        if (!str) {
            return E_AMF_TYPE_ERROR;
        }

        MRtmpContext *ctx = m_protocol->getRtmpCtx();
        ctx->setStreamName(str->var);
        MString url = ctx->rtmpUrl->url();

        MString vhost = ctx->rtmpUrl->vhost();
        MString mode = BlsConf::instance()->getMode(vhost);
        MString fullUrl = ctx->rtmpUrl->fullUrl();

        bool hasBackSource = BlsBackSource::instance()->hasBackSource(fullUrl);
        if (!hasBackSource) {
            // TODO if fails ?
            MString res;
            g_cchannel->sendLineAndWaitResponse(Internal_CMD_WhoHasBackSource, url, res);
            int port = getValue(res).toInt();

            if (mode == Mode_Remote) {
                if (port == 0) {
                    // back source to local server
                    BlsBackSource::instance()->add(ctx->rtmpUrl->vhost(), ctx->rtmpUrl->port(), ctx->rtmpUrl->app(), ctx->rtmpUrl->fullUrl());
                    log_trace("begin back source to %s:%d pid=%d", ctx->rtmpUrl->vhost().c_str(), ctx->rtmpUrl->port(), getpid());
                } else if (port > 0) {
                    // back source to origin server
                    BlsBackSource::instance()->add("127.0.0.1", port, ctx->rtmpUrl->app(), ctx->rtmpUrl->fullUrl());
                    log_trace("begin back source to %s:%d pid=%d", "127.0.0.1", port, getpid());
                }
            } else if (mode == Mode_Local) {
                BlsBackSource::instance()->add("127.0.0.1", port, ctx->rtmpUrl->app(), ctx->rtmpUrl->fullUrl());      // back source to origin server
            }
        }

        log_trace("start play : %s", fullUrl.c_str());

        m_source = MRtmpSource::findSource(url);
        m_role = Role_Connection_Play;
        playService();

    } else if (name == "_checkbw") {
        return ret;
    }

    else {
        log_error("MRtmpConnection onCommand : no method \"%s\"", name.c_str());
        return E_INVOKE_NO_METHOD;
    }

    return ret;
}