void srs_vhost_resolve(std::string& vhost, std::string& app)
{
    app = srs_replace(app, "...", "?");
    
    size_t pos = 0;
    if ((pos = app.find("?")) == std::string::npos) {
        return;
    }
    
    std::string query = app.substr(pos + 1);
    app = app.substr(0, pos);
    
    if ((pos = query.find("vhost?")) != std::string::npos
        || (pos = query.find("vhost=")) != std::string::npos
        || (pos = query.find("Vhost?")) != std::string::npos
        || (pos = query.find("Vhost=")) != std::string::npos
    ) {
        query = query.substr(pos + 6);
        if (!query.empty()) {
            vhost = query;
        }
    }
}
int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app, std::string stream, SrsConfDirective* engine)
{
	int ret = ERROR_SUCCESS;
	
	config->get_engine_vfilter(engine, vfilter);
	vcodec 			= config->get_engine_vcodec(engine);
	vbitrate 		= config->get_engine_vbitrate(engine);
	vfps 			= config->get_engine_vfps(engine);
	vwidth 			= config->get_engine_vwidth(engine);
	vheight 		= config->get_engine_vheight(engine);
	vthreads 		= config->get_engine_vthreads(engine);
	vprofile 		= config->get_engine_vprofile(engine);
	vpreset 		= config->get_engine_vpreset(engine);
	config->get_engine_vparams(engine, vparams);
	acodec 			= config->get_engine_acodec(engine);
	abitrate 		= config->get_engine_abitrate(engine);
	asample_rate 	= config->get_engine_asample_rate(engine);
	achannels 		= config->get_engine_achannels(engine);
	config->get_engine_aparams(engine, aparams);
	output 			= config->get_engine_output(engine);
	
	// ensure the size is even.
	vwidth -= vwidth % 2;
	vheight -= vheight % 2;

	// input stream, from local.
	// ie. rtmp://127.0.0.1:1935/live/livestream
	input = "rtmp://127.0.0.1:";
	input += port;
	input += "/";
	input += app;
	input += "/";
	input += stream;
	
	// output stream, to other/self server
	// ie. rtmp://127.0.0.1:1935/live/livestream_sd
	if (vhost == RTMP_VHOST_DEFAULT) {
		output = srs_replace(output, "[vhost]", "127.0.0.1");
	} else {
		output = srs_replace(output, "[vhost]", vhost);
	}
	output = srs_replace(output, "[port]", port);
	output = srs_replace(output, "[app]", app);
	output = srs_replace(output, "[stream]", stream);

	// important: loop check, donot transcode again.
	// we think the following is loop circle:
	// input: rtmp://127.0.0.1:1935/live/livestream_sd
	// output: rtmp://127.0.0.1:1935/live/livestream_sd_sd
	std::string tail = ""; // tail="_sd"
	if (output.length() > input.length()) {
		tail = output.substr(input.length());
	}
	// TODO: better dead loop check. 
	// if input also endwiths the tail, loop detected.
	if (!tail.empty() && input.rfind(tail) == input.length() - tail.length()) {
		ret = ERROR_ENCODER_LOOP;
		srs_info("detect a loop cycle, input=%s, output=%s, ignore it. ret=%d",
			input.c_str(), output.c_str(), ret);
		return ret;
	}
	
	if (vcodec != SRS_ENCODER_VCODEC) {
		ret = ERROR_ENCODER_VCODEC;
		srs_error("invalid vcodec, must be %s, actual %s, ret=%d",
			SRS_ENCODER_VCODEC, vcodec.c_str(), ret);
		return ret;
	}
	if (vbitrate <= 0) {
		ret = ERROR_ENCODER_VBITRATE;
		srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);
		return ret;
	}
	if (vfps <= 0) {
		ret = ERROR_ENCODER_VFPS;
		srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);
		return ret;
	}
	if (vwidth <= 0) {
		ret = ERROR_ENCODER_VWIDTH;
		srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);
		return ret;
	}
	if (vheight <= 0) {
		ret = ERROR_ENCODER_VHEIGHT;
		srs_error("invalid vheight: %d, ret=%d", vheight, ret);
		return ret;
	}
	if (vthreads < 0) {
		ret = ERROR_ENCODER_VTHREADS;
		srs_error("invalid vthreads: %d, ret=%d", vthreads, ret);
		return ret;
	}
	if (vprofile.empty()) {
		ret = ERROR_ENCODER_VPROFILE;
		srs_error("invalid vprofile: %s, ret=%d", vprofile.c_str(), ret);
		return ret;
	}
	if (vpreset.empty()) {
		ret = ERROR_ENCODER_VPRESET;
		srs_error("invalid vpreset: %s, ret=%d", vpreset.c_str(), ret);
		return ret;
	}
	if (acodec != SRS_ENCODER_ACODEC) {
		ret = ERROR_ENCODER_ACODEC;
		srs_error("invalid acodec, must be %s, actual %s, ret=%d",
			SRS_ENCODER_ACODEC, acodec.c_str(), ret);
		return ret;
	}
	if (abitrate <= 0) {
		ret = ERROR_ENCODER_ABITRATE;
		srs_error("invalid abitrate: %d, ret=%d", 
			abitrate, ret);
		return ret;
	}
	if (asample_rate <= 0) {
		ret = ERROR_ENCODER_ASAMPLE_RATE;
		srs_error("invalid sample rate: %d, ret=%d", 
			asample_rate, ret);
		return ret;
	}
	if (achannels != 1 && achannels != 2) {
		ret = ERROR_ENCODER_ACHANNELS;
		srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d", 
			achannels, ret);
		return ret;
	}
	if (output.empty()) {
		ret = ERROR_ENCODER_OUTPUT;
		srs_error("invalid empty output, ret=%d", ret);
		return ret;
	}
	
	return ret;
}