int SrsEdgeIngester::process_publish_message(SrsMessage* msg)
{
    int ret = ERROR_SUCCESS;
    
    SrsSource* source = _source;
        
    // process audio packet
    if (msg->header.is_audio()) {
        if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) {
            srs_error("source process audio message failed. ret=%d", ret);
            return ret;
        }
    }
    
    // process video packet
    if (msg->header.is_video()) {
        if ((ret = source->on_video(msg)) != ERROR_SUCCESS) {
            srs_error("source process video message failed. ret=%d", ret);
            return ret;
        }
    }
    
    // process aggregate packet
    if (msg->header.is_aggregate()) {
        if ((ret = source->on_aggregate(msg)) != ERROR_SUCCESS) {
            srs_error("source process aggregate message failed. ret=%d", ret);
            return ret;
        }
        return ret;
    }

    // process onMetaData
    if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
        SrsPacket* pkt = NULL;
        if ((ret = client->decode_message(msg, &pkt)) != ERROR_SUCCESS) {
            srs_error("decode onMetaData message failed. ret=%d", ret);
            return ret;
        }
        SrsAutoFree(SrsPacket, pkt);
    
        if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) {
            SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt);
            if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) {
                srs_error("source process onMetaData message failed. ret=%d", ret);
                return ret;
            }
            srs_info("process onMetaData message success.");
            return ret;
        }
        
        srs_info("ignore AMF0/AMF3 data message.");
        return ret;
    }
    
    return ret;
}
int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name)
{
	int ret = ERROR_SUCCESS;
	
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id);
		
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send createStream response message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send createStream response message success.");
	}
	
	while (true) {
		SrsCommonMessage* msg = NULL;
		if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
			srs_error("recv identify client message failed. ret=%d", ret);
			return ret;
		}

		SrsAutoFree(SrsCommonMessage, msg, false);

		if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
			srs_trace("identify ignore messages except "
				"AMF0/AMF3 command message. type=%#x", msg->header.message_type);
			continue;
		}
		
		if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
			srs_error("identify decode message failed. ret=%d", ret);
			return ret;
		}
		
		SrsPacket* pkt = msg->get_packet();
		if (dynamic_cast<SrsPlayPacket*>(pkt)) {
			SrsPlayPacket* play = dynamic_cast<SrsPlayPacket*>(pkt);
			type = SrsClientPlay;
			stream_name = play->stream_name;
			srs_trace("identity client type=play, stream_name=%s", stream_name.c_str());
			return ret;
		}
		if (dynamic_cast<SrsPublishPacket*>(pkt)) {
			srs_info("identify client by publish, falsh publish.");
			return identify_flash_publish_client(
				dynamic_cast<SrsPublishPacket*>(pkt), type, stream_name);
		}
		
		srs_trace("ignore AMF0/AMF3 command message.");
	}
	
	return ret;
}
int SrsRtmpClient::connect_app(std::string app, std::string tc_url)
{
	int ret = ERROR_SUCCESS;
	
	// Connect(vhost, app)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsConnectAppPacket* pkt = new SrsConnectAppPacket();
		msg->set_packet(pkt, 0);
		
		pkt->command_object = new SrsAmf0Object();
		pkt->command_object->set("app", new SrsAmf0String(app.c_str()));
		pkt->command_object->set("swfUrl", new SrsAmf0String());
		pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str()));
		pkt->command_object->set("fpad", new SrsAmf0Boolean(false));
		pkt->command_object->set("capabilities", new SrsAmf0Number(239));
		pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575));
		pkt->command_object->set("videoCodecs", new SrsAmf0Number(252));
		pkt->command_object->set("videoFunction", new SrsAmf0Number(1));
		pkt->command_object->set("pageUrl", new SrsAmf0String());
		pkt->command_object->set("objectEncoding", new SrsAmf0Number(0));
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			return ret;
		}
	}
	
	// Set Window Acknowledgement size(2500000)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
	
		pkt->ackowledgement_window_size = 2500000;
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			return ret;
		}
	}
	
	// expect connect _result
	SrsCommonMessage* msg = NULL;
	SrsConnectAppResPacket* pkt = NULL;
	if ((ret = srs_rtmp_expect_message<SrsConnectAppResPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
		srs_error("expect connect app response message failed. ret=%d", ret);
		return ret;
	}
	SrsAutoFree(SrsCommonMessage, msg, false);
	srs_info("get connect app response message");
	
    return ret;
}
int SrsEdgeIngester::ingest()
{
    int ret = ERROR_SUCCESS;
    
    client->set_recv_timeout(SRS_EDGE_INGESTER_TIMEOUT_US);
    
    SrsPithyPrint pithy_print(SRS_CONSTS_STAGE_EDGE);

    while (pthread->can_loop()) {
        // switch to other st-threads.
        st_usleep(0);
        
        pithy_print.elapse();
        
        // pithy print
        if (pithy_print.can_print()) {
            kbps->sample();
            srs_trace("<- "SRS_CONSTS_LOG_EDGE_PLAY
                " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", 
                pithy_print.age(),
                kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(),
                kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m());
        }

        // read from client.
        SrsMessage* msg = NULL;
        if ((ret = client->recv_message(&msg)) != ERROR_SUCCESS) {
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("pull origin server message failed. ret=%d", ret);
            }
            return ret;
        }
        srs_verbose("edge loop recv message. ret=%d", ret);
        
        srs_assert(msg);
        SrsAutoFree(SrsMessage, msg);
        
        if ((ret = process_publish_message(msg)) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    return ret;
}
int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name)
{
	type = SrsClientUnknown;
	int ret = ERROR_SUCCESS;
	
	while (true) {
		SrsCommonMessage* msg = NULL;
		if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
			srs_error("recv identify client message failed. ret=%d", ret);
			return ret;
		}

		SrsAutoFree(SrsCommonMessage, msg, false);

		if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) {
			srs_trace("identify ignore messages except "
				"AMF0/AMF3 command message. type=%#x", msg->header.message_type);
			continue;
		}
		
		if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) {
			srs_error("identify decode message failed. ret=%d", ret);
			return ret;
		}
		
		SrsPacket* pkt = msg->get_packet();
		if (dynamic_cast<SrsCreateStreamPacket*>(pkt)) {
			srs_info("identify client by create stream, play or flash publish.");
			return identify_create_stream_client(
				dynamic_cast<SrsCreateStreamPacket*>(pkt), stream_id, type, stream_name);
		}
		if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {
			srs_info("identify client by releaseStream, fmle publish.");
			return identify_fmle_publish_client(
				dynamic_cast<SrsFMLEStartPacket*>(pkt), type, stream_name);
		}
		
		srs_trace("ignore AMF0/AMF3 command message.");
	}
	
	return ret;
}
int SrsRtmp::connect_app(SrsRequest* req)
{
	int ret = ERROR_SUCCESS;
	
	SrsCommonMessage* msg = NULL;
	SrsConnectAppPacket* pkt = NULL;
	if ((ret = srs_rtmp_expect_message<SrsConnectAppPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
		srs_error("expect connect app message failed. ret=%d", ret);
		return ret;
	}
	SrsAutoFree(SrsCommonMessage, msg, false);
	srs_info("get connect app message");
	
	SrsAmf0Any* prop = NULL;
	
	if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) {
		ret = ERROR_RTMP_REQ_CONNECT;
		srs_error("invalid request, must specifies the tcUrl. ret=%d", ret);
		return ret;
	}
	req->tcUrl = srs_amf0_convert<SrsAmf0String>(prop)->value;
	
	if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) {
		req->pageUrl = srs_amf0_convert<SrsAmf0String>(prop)->value;
	}
	
	if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) {
		req->swfUrl = srs_amf0_convert<SrsAmf0String>(prop)->value;
	}
	
	if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) {
		req->objectEncoding = srs_amf0_convert<SrsAmf0Number>(prop)->value;
	}
	
	srs_info("get connect app message params success.");
	
	return req->discovery_app();
}
int SrsSource::on_video(SrsCommonMessage* video)
{
	int ret = ERROR_SUCCESS;
	
	SrsSharedPtrMessage* msg = new SrsSharedPtrMessage();
	SrsAutoFree(SrsSharedPtrMessage, msg, false);
	if ((ret = msg->initialize(video)) != ERROR_SUCCESS) {
		srs_error("initialize the video failed. ret=%d", ret);
		return ret;
	}
	srs_verbose("initialize shared ptr video success.");
	
#ifdef SRS_HLS
	if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) {
		srs_error("hls process video message failed. ret=%d", ret);
		return ret;
	}
#endif
	
	// copy to all consumer
	if (true) {
		std::vector<SrsConsumer*>::iterator it;
		for (it = consumers.begin(); it != consumers.end(); ++it) {
			SrsConsumer* consumer = *it;
			if ((ret = consumer->enqueue(msg->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) {
				srs_error("dispatch the video failed. ret=%d", ret);
				return ret;
			}
		}
		srs_info("dispatch video success.");
	}

	// copy to all forwarders.
	if (true) {
		std::vector<SrsForwarder*>::iterator it;
		for (it = forwarders.begin(); it != forwarders.end(); ++it) {
			SrsForwarder* forwarder = *it;
			if ((ret = forwarder->on_video(msg->copy())) != ERROR_SUCCESS) {
				srs_error("forwarder process video message failed. ret=%d", ret);
				return ret;
			}
		}
	}

	// cache the sequence header if h264
	if (SrsCodec::video_is_sequence_header(msg->payload, msg->size)) {
		srs_freep(cache_sh_video);
		cache_sh_video = msg->copy();
		srs_trace("update video sequence header success. size=%d", msg->header.payload_length);
		return ret;
	}

	// cache the last gop packets
	if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) {
		srs_error("shrink gop cache failed. ret=%d", ret);
		return ret;
	}
	srs_verbose("cache gop success.");
	
	return ret;
}
int SrsRtmp::start_fmle_publish(int stream_id)
{
	int ret = ERROR_SUCCESS;
	
	// FCPublish
	double fc_publish_tid = 0;
	if (true) {
		SrsCommonMessage* msg = NULL;
		SrsFMLEStartPacket* pkt = NULL;
		if ((ret = srs_rtmp_expect_message<SrsFMLEStartPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
			srs_error("recv FCPublish message failed. ret=%d", ret);
			return ret;
		}
		srs_info("recv FCPublish request message success.");
		
		SrsAutoFree(SrsCommonMessage, msg, false);
		fc_publish_tid = pkt->transaction_id;
	}
	// FCPublish response
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid);
		
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send FCPublish response message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send FCPublish response message success.");
	}
	
	// createStream
	double create_stream_tid = 0;
	if (true) {
		SrsCommonMessage* msg = NULL;
		SrsCreateStreamPacket* pkt = NULL;
		if ((ret = srs_rtmp_expect_message<SrsCreateStreamPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
			srs_error("recv createStream message failed. ret=%d", ret);
			return ret;
		}
		srs_info("recv createStream request message success.");
		
		SrsAutoFree(SrsCommonMessage, msg, false);
		create_stream_tid = pkt->transaction_id;
	}
	// createStream response
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id);
		
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send createStream response message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send createStream response message success.");
	}
	
	// publish
	if (true) {
		SrsCommonMessage* msg = NULL;
		SrsPublishPacket* pkt = NULL;
		if ((ret = srs_rtmp_expect_message<SrsPublishPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
			srs_error("recv publish message failed. ret=%d", ret);
			return ret;
		}
		srs_info("recv publish request message success.");
		
		SrsAutoFree(SrsCommonMessage, msg, false);
	}
	// publish response onFCPublish(NetStream.Publish.Start)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
		
		pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH;
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
		pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onFCPublish(NetStream.Publish.Start) message success.");
	}
	// publish response onStatus(NetStream.Publish.Start)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
		
		pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
		pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
		pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onStatus(NetStream.Publish.Start) message success.");
	}
	
	srs_info("FMLE publish success.");
	
	return ret;
}