예제 #1
0
파일: yle.c 프로젝트: qtportal/yle-dl
int ExecuteRPCResult(RTMP *r, AMFObject *obj, YLENGStream *yle, int *redirected) {
  AVal rpcKind;
  AVal mediaxml;
  char *playurl, *tvpayOnly;
  AVal parsedHost = {NULL, 0};
  AVal parsedPlaypath = {NULL, 0};
  AVal parsedApp = {NULL, 0};

  *redirected = FALSE;

  AMFProp_GetString(AMF_GetProp(obj, NULL, 3), &rpcKind);
  
  if (!AVMATCH(&rpcKind, &av_e0))
    return TRUE;

  AMFProp_GetString(AMF_GetProp(obj, NULL, 4), &mediaxml);
  RTMP_Log(RTMP_LOGDEBUG, "clip data:\n%.*s", mediaxml.av_len, mediaxml.av_val);

  playurl = GetXMLNodeContent(mediaxml.av_val, "url");
  if (!playurl)
    return FALSE;

  if (!ParseYLEPlaypath(playurl, &parsedHost, &parsedApp, &parsedPlaypath)) {
    RTMP_Log(RTMP_LOGERROR, "Couldn't parse stream url %s!", playurl);
    free(playurl);
    return FALSE;
  }

  // FIXME: old r->Link.playpath may be leaked
  r->Link.playpath.av_len = parsedPlaypath.av_len;
  r->Link.playpath.av_val = malloc(parsedPlaypath.av_len*sizeof(char));
  strncpy(r->Link.playpath.av_val, parsedPlaypath.av_val, r->Link.playpath.av_len);

  RTMP_Log(RTMP_LOGDEBUG, "New playpath   : %.*s",
           r->Link.playpath.av_len, r->Link.playpath.av_val);

  if (!AVMATCH(&parsedHost, &r->Link.hostname)) {
    RTMP_Log(RTMP_LOGDEBUG, "Redirected to another server: %.*s",
             parsedHost.av_len, parsedHost.av_val);

    // FIXME: old value may be leaked
    r->Link.hostname.av_val = malloc(parsedHost.av_len*sizeof(char));
    r->Link.hostname.av_len = parsedHost.av_len;
    memcpy(r->Link.hostname.av_val, parsedHost.av_val, parsedHost.av_len);

    *redirected = TRUE;
  }

  tvpayOnly = GetXMLNodeContent(mediaxml.av_val, "tvpayOnly");
  if (tvpayOnly) {
    yle->tvFeeRequired = (strcmp(tvpayOnly, "false")!=0);
    free(tvpayOnly);
  }

  free(playurl);

  return RTMP_SendCreateStream(r);
}
예제 #2
0
파일: yle.c 프로젝트: qtportal/yle-dl
int ExecuteAuthenticationDetails(RTMP *r, AMFObject *proplist, YLENGStream *yle) {
  long authResult = -1;
  int i;
  
  for (i=0; i<AMF_CountProp(proplist); i++) {
    AVal name;
    AMFObjectProperty *prop = AMF_GetProp(proplist, NULL, i);
    AMFProp_GetName(prop, &name);

    if (AVMATCH(&name, &av_locatedInBroadcastTerritory)) {
      yle->locatedInBroadcastTerritory = AMFProp_GetBoolean(prop);
    } else if (AVMATCH(&name, &av_randomAuth)) {
      authResult = ((long)AMFProp_GetNumber(prop) + 447537687) % 6834253;
    } else if (AVMATCH(&name, &av_tvFeeActivated)) {
      yle->tvFeeActivated = AMFProp_GetBoolean(prop);
    }
  }

  if (authResult != -1) {
    RTMPPacket packet;
    char pbuf[128], *pend = pbuf+sizeof(pbuf);

    packet.m_nChannel = 0x03;   // control channel
    packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet.m_packetType = 0x11; // FLEX MESSAGE
    packet.m_nTimeStamp = RTMP_GetTime();
    packet.m_nInfoField2 = 0;
    packet.m_hasAbsTimestamp = 0;

    packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
    char *enc = packet.m_body;
    *enc++ = 0x00;   // Unknown
    enc = AMF_EncodeString(enc, pend, &av_authenticateRandomNumber);
    enc = AMF_EncodeNumber(enc, pend, 0);
    *enc++ = AMF_NULL;
    enc = AMF_EncodeNumber(enc, pend, (double)authResult);

    packet.m_nBodySize = enc-packet.m_body;

    RTMP_Log(RTMP_LOGDEBUG, "sending authenticateRandomNumber");

    return RTMP_SendPacket(r, &packet, FALSE);
  }

  return FALSE;
}
예제 #3
0
파일: rtspd.c 프로젝트: LinLL/ipc
SPOOK_SESSION_PROBE_t RTSPD_probe(const void* msg, ssize_t msg_sz)
{
	int ret = 0;
	AVal av_options = AVC("OPTIONS");
	AVal av_describe = AVC("DESCRIBE");
	AVal av_set_parameter = AVC("SET_PARAMETER");
	AVal av_get_parameter = AVC("GET_PARAMETER");
	//
	AVal av_parser = RTSPPARSER_create((const char*)msg, msg_sz);
	AVal av_cmd = RTSPPARSER_get_command(av_parser);
	
	if(AVMATCH(&av_cmd, &av_options)
		|| AVMATCH(&av_cmd, &av_describe)
		|| AVMATCH(&av_cmd, &av_set_parameter)
		|| AVMATCH(&av_cmd, &av_get_parameter)){
		return SPOOK_PROBE_MATCH;
	}
	return SPOOK_PROBE_MISMATCH;
}
예제 #4
0
파일: yle.c 프로젝트: qtportal/yle-dl
int ExecuteInvokedMethod(RTMP *r, const AVal *method, AMFObject *obj, void *ctx) {
  struct YLENGStream *yle = (struct YLENGStream *)ctx;
  int redirected = FALSE;

  if (!yle || yle->yleAuth == 0)
    return RTMP_CB_NOT_HANDLED;

  if (AVMATCH(method, &av_authenticationDetails)) {
    AMFObject list;
    AMFProp_GetObject(AMF_GetProp(obj, NULL, 3), &list);
    if (!ExecuteAuthenticationDetails(r, &list, yle))
      return RTMP_CB_ERROR_STOP;
    
    if (yle->clipID.av_len) {
      if (!RequestData(r, yle))
        return RTMP_CB_ERROR_STOP;
    } else if (!RTMP_SendCreateStream(r)) {
      return RTMP_CB_ERROR_STOP;
    }
        
    return RTMP_CB_SUCCESS;

  } else if (AVMATCH(method, &av_randomNumberAuthenticated)) {
    ExecuteRandomNumberAuthenticated(r);
    return RTMP_CB_SUCCESS;

  } else if (AVMATCH(method, &av_rpcResult)) {
    if (!ExecuteRPCResult(r, obj, yle, &redirected))
      return RTMP_CB_ERROR_STOP;

    //if (redirected && !ConnectRedirected(r, r->Link.seekTime, yle))
    //  return RTMP_CB_ERROR_STOP;

    return RTMP_CB_SUCCESS;

  } else if (AVMATCH(method, &av_rpcError)) {
    RTMP_Log(RTMP_LOGERROR, "RTMP server returned RPC error");
    return RTMP_CB_ERROR_STOP;
  }

  return RTMP_CB_NOT_HANDLED;
}
예제 #5
0
파일: rtmp.c 프로젝트: lprot/showtime
static int
handle_metadata0(rtmp_t *r, AMFObject *obj,
		 media_pipe_t *mp, char *errstr, size_t errlen)
{
  AVal metastring;
  AMFObjectProperty prop;
  prop_t *m = mp->mp_prop_metadata;


  AMFProp_GetString(AMF_GetProp(obj, NULL, 0), &metastring);

  if(!AVMATCH(&metastring, &av_onMetaData)) {
    snprintf(errstr, errlen, "No metadata in metadata packet");
    return -1;
  }
  if(RTMP_FindFirstMatchingProperty(obj, &av_duration, &prop) &&
     prop.p_type == AMF_NUMBER && prop.p_vu.p_number > 0) {
    prop_set_float(prop_create(m, "duration"), prop.p_vu.p_number);
    r->total_duration = prop.p_vu.p_number * 1000;
    mp_set_duration(mp, r->total_duration * 1000LL);
    mp_set_clr_flags(mp, MP_CAN_SEEK, 0);

    if(r->ss == NULL && !(r->va->flags & BACKEND_VIDEO_NO_SUBTITLE_SCAN))
      r->ss = sub_scanner_create(r->url, mp->mp_prop_subtitle_tracks, r->va, 0);

  } else {

    r->total_duration = 0;
    mp_set_duration(mp, AV_NOPTS_VALUE);
    mp_set_clr_flags(mp, 0, MP_CAN_SEEK);
  }

  if((RTMP_FindFirstMatchingProperty(obj, &av_videoframerate, &prop) &&
      RTMP_FindFirstMatchingProperty(obj, &av_framerate, &prop))
     && prop.p_type == AMF_NUMBER) {
    r->vframeduration = 1000000.0 / prop.p_vu.p_number;
    mp->mp_framerate.num = 1000000;
    mp->mp_framerate.den = prop.p_vu.p_number;
  }
  r->width = r->height = 0;
  if(RTMP_FindFirstMatchingProperty(obj, &av_width, &prop) &&
     prop.p_type == AMF_NUMBER)
    r->width = prop.p_vu.p_number;

  if(RTMP_FindFirstMatchingProperty(obj, &av_height, &prop) &&
     prop.p_type == AMF_NUMBER)
    r->height = prop.p_vu.p_number;

  if(r->width && r->height)
    TRACE(TRACE_DEBUG, "RTMP", "Video size %d x %d", r->width, r->height);
  return 0;

}
예제 #6
0
파일: yle.c 프로젝트: qtportal/yle-dl
int ConnectYLE(RTMP *r, const AVal *methodInvoked, AMFObject *obj, void *ctx) {
  struct YLENGStream *yle = (struct YLENGStream *)ctx;

  if (!yle || !AVMATCH(methodInvoked, &av_connect) || yle->yleAuth == 0)
    return RTMP_CB_NOT_HANDLED;

  /* Like connection handling in librtmp but don't send
     Ctrl or CreateStream. */

  RTMP_SendServerBW(r);
  /*RTMP_SendCtrl(r, 3, 0, 300);*/

  return RTMP_CB_SUCCESS;
}
예제 #7
0
파일: rtmp.c 프로젝트: aznboi665/showtime
static int
handle_metadata0(rtmp_t *r, AMFObject *obj,
		 media_pipe_t *mp, char *errstr, size_t errlen)
{
  AVal metastring;
  AMFObjectProperty prop;
  prop_t *m = mp->mp_prop_metadata;


  AMFProp_GetString(AMF_GetProp(obj, NULL, 0), &metastring);

  if(!AVMATCH(&metastring, &av_onMetaData)) {
    snprintf(errstr, errlen, "No metadata in metadata packet");
    return -1;
  }
  if(!RTMP_FindFirstMatchingProperty(obj, &av_duration, &prop) ||
     prop.p_type != AMF_NUMBER) {
    snprintf(errstr, errlen, "Unable to parse total duration");
    return -1;
  }
  prop_set_float(prop_create(m, "duration"), prop.p_vu.p_number);
  r->duration = prop.p_vu.p_number;



  if(!RTMP_FindFirstMatchingProperty(obj, &av_videoframerate, &prop) ||
     prop.p_type != AMF_NUMBER) {

    if(!RTMP_FindFirstMatchingProperty(obj, &av_framerate, &prop) ||
       prop.p_type != AMF_NUMBER) {
      snprintf(errstr, errlen, "Unable to parse video framerate");
      return -1;
    }
  }
  r->vframeduration = 1000000.0 / prop.p_vu.p_number;

  r->width = r->height = 0;
  if(RTMP_FindFirstMatchingProperty(obj, &av_width, &prop) &&
     prop.p_type == AMF_NUMBER)
    r->width = prop.p_vu.p_number;

  if(RTMP_FindFirstMatchingProperty(obj, &av_height, &prop) &&
     prop.p_type == AMF_NUMBER)
    r->height = prop.p_vu.p_number;

  if(r->width && r->height)
    TRACE(TRACE_DEBUG, "RTMP", "Video size %d x %d", r->width, r->height);
  return 0;

}
예제 #8
0
int
OpenResumeFile(const char *flvFile,	// file name [in]
			   FILE ** file,	// opened file [out]
			   off_t * size,	// size of the file [out]
			   char **metaHeader,	// meta data read from the file [out]
			   uint32_t * nMetaHeaderSize,	// length of metaHeader [out]
			   double *duration)	// duration of the stream in ms [out]
{
	size_t bufferSize = 0;
	char hbuf[16], *buffer = NULL;

	*nMetaHeaderSize = 0;
	*size = 0;

	*file = fopen(flvFile, "r+b");
	if (!*file)
		return RD_SUCCESS;		// RD_SUCCESS, because we go to fresh file mode instead of quiting

	fseek(*file, 0, SEEK_END);
	*size = ftello(*file);
	fseek(*file, 0, SEEK_SET);

	if (*size > 0)
	{
		// verify FLV format and read header
		uint32_t prevTagSize = 0;

		// check we've got a valid FLV file to continue!
		if (fread(hbuf, 1, 13, *file) != 13)
		{
			RTMP_Log(RTMP_LOGERROR, "Couldn't read FLV file header!");
			return RD_FAILED;
		}
		if (hbuf[0] != 'F' || hbuf[1] != 'L' || hbuf[2] != 'V'
			|| hbuf[3] != 0x01)
		{
			RTMP_Log(RTMP_LOGERROR, "Invalid FLV file!");
			return RD_FAILED;
		}

		if ((hbuf[4] & 0x05) == 0)
		{
			RTMP_Log(RTMP_LOGERROR,
					 "FLV file contains neither video nor audio, aborting!");
			return RD_FAILED;
		}

		uint32_t dataOffset = AMF_DecodeInt32(hbuf + 5);
		fseek(*file, dataOffset, SEEK_SET);

		if (fread(hbuf, 1, 4, *file) != 4)
		{
			RTMP_Log(RTMP_LOGERROR, "Invalid FLV file: missing first prevTagSize!");
			return RD_FAILED;
		}
		prevTagSize = AMF_DecodeInt32(hbuf);
		if (prevTagSize != 0)
		{
			RTMP_Log(RTMP_LOGWARNING,
					 "First prevTagSize is not zero: prevTagSize = 0x%08X",
					 prevTagSize);
		}

		// go through the file to find the meta data!
		off_t pos = dataOffset + 4;
		int bFoundMetaHeader = FALSE;

		while (pos < *size - 4 && !bFoundMetaHeader)
		{
			fseeko(*file, pos, SEEK_SET);
			if (fread(hbuf, 1, 4, *file) != 4)
				break;

			uint32_t dataSize = AMF_DecodeInt24(hbuf + 1);

			if (hbuf[0] == 0x12)
			{
				if (dataSize > bufferSize)
				{
					/* round up to next page boundary */
					bufferSize = dataSize + 4095;
					bufferSize ^= (bufferSize & 4095);
					free(buffer);
					buffer = (char *)malloc(bufferSize);
					if (!buffer)
						return RD_FAILED;
				}

				fseeko(*file, pos + 11, SEEK_SET);
				if (fread(buffer, 1, dataSize, *file) != dataSize)
					break;

				AMFObject metaObj;
				int nRes = AMF_Decode(&metaObj, buffer, dataSize, FALSE);
				if (nRes < 0)
				{
					RTMP_Log(RTMP_LOGERROR, "%s, error decoding meta data packet",
							 __FUNCTION__);
					break;
				}

				AVal metastring;
				AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring);

				if (AVMATCH(&metastring, &av_onMetaData))
				{
					AMF_Dump(&metaObj);

					*nMetaHeaderSize = dataSize;
					if (*metaHeader)
						free(*metaHeader);
					*metaHeader = (char *) malloc(*nMetaHeaderSize);
					memcpy(*metaHeader, buffer, *nMetaHeaderSize);

					// get duration
					AMFObjectProperty prop;
					if (RTMP_FindFirstMatchingProperty
						(&metaObj, &av_duration, &prop))
					{
						*duration = AMFProp_GetNumber(&prop);
						RTMP_Log(RTMP_LOGDEBUG, "File has duration: %f", *duration);
					}

					bFoundMetaHeader = TRUE;
					break;
				}
				//metaObj.Reset();
				//delete obj;
			}
			pos += (dataSize + 11 + 4);
		}

		free(buffer);
		if (!bFoundMetaHeader)
			RTMP_Log(RTMP_LOGWARNING, "Couldn't locate meta data!");
	}

	return RD_SUCCESS;
}
예제 #9
0
파일: rtmpsrv.c 프로젝트: firemyst/rtmpdump
// Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
int
ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
{
  const char *body;
  unsigned int nBodySize;
  int ret = 0, nRes;

  body = packet->m_body + offset;
  nBodySize = packet->m_nBodySize - offset;

  if (body[0] != 0x02)		// make sure it is a string method name we start with
    {
      RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
	  __FUNCTION__);
      return 0;
    }

  AMFObject obj;
  nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
  if (nRes < 0)
    {
      RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
      return 0;
    }

  AMF_Dump(&obj);
  AVal method;
  AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
  double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
  RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val);

  if (AVMATCH(&method, &av_connect))
    {
      AMFObject cobj;
      AVal pname, pval;
      int i;

      server->connect = packet->m_body;
      packet->m_body = NULL;

      AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
      for (i=0; i<cobj.o_num; i++)
	{
	  pname = cobj.o_props[i].p_name;
	  pval.av_val = NULL;
	  pval.av_len = 0;
	  if (cobj.o_props[i].p_type == AMF_STRING)
	    pval = cobj.o_props[i].p_vu.p_aval;
	  if (AVMATCH(&pname, &av_app))
	    {
	      r->Link.app = pval;
	      pval.av_val = NULL;
	      if (!r->Link.app.av_val)
	        r->Link.app.av_val = "";
	      server->arglen += 6 + pval.av_len;
	      server->argc += 2;
	    }
	  else if (AVMATCH(&pname, &av_flashVer))
	    {
	      r->Link.flashVer = pval;
	      pval.av_val = NULL;
	      server->arglen += 6 + pval.av_len;
	      server->argc += 2;
	    }
	  else if (AVMATCH(&pname, &av_swfUrl))
	    {
	      r->Link.swfUrl = pval;
	      pval.av_val = NULL;
	      server->arglen += 6 + pval.av_len;
	      server->argc += 2;
	    }
	  else if (AVMATCH(&pname, &av_tcUrl))
	    {
	      r->Link.tcUrl = pval;
	      pval.av_val = NULL;
	      server->arglen += 6 + pval.av_len;
	      server->argc += 2;
	    }
	  else if (AVMATCH(&pname, &av_pageUrl))
	    {
	      r->Link.pageUrl = pval;
	      pval.av_val = NULL;
	      server->arglen += 6 + pval.av_len;
	      server->argc += 2;
	    }
	  else if (AVMATCH(&pname, &av_audioCodecs))
	    {
	      r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number;
	    }
	  else if (AVMATCH(&pname, &av_videoCodecs))
	    {
	      r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number;
	    }
	  else if (AVMATCH(&pname, &av_objectEncoding))
	    {
	      r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
	    }
	}
      /* Still have more parameters? Copy them */
      if (obj.o_num > 3)
	{
	  int i = obj.o_num - 3;
	  r->Link.extras.o_num = i;
	  r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
	  memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
	  obj.o_num = 3;
	  server->arglen += countAMF(&r->Link.extras, &server->argc);
	}
      SendConnectResult(r, txn);
    }
  else if (AVMATCH(&method, &av_createStream))
    {
      SendResultNumber(r, txn, ++server->streamID);
    }
  else if (AVMATCH(&method, &av_getStreamLength))
    {
      SendResultNumber(r, txn, 10.0);
    }
  else if (AVMATCH(&method, &av_play))
    {
      char *file, *p, *q, *cmd, *ptr;
      AVal *argv, av;
      int len, argc;
      uint32_t now;
      RTMPPacket pc = {0};
      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
      /*
      r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
      if (obj.o_num > 5)
	r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
      */
      if (r->Link.tcUrl.av_len)
	{
	  len = server->arglen + r->Link.playpath.av_len + 4 +
	    sizeof("rtmpdump") + r->Link.playpath.av_len + 12;
	  server->argc += 5;

	  cmd = malloc(len + server->argc * sizeof(AVal));
	  ptr = cmd;
	  argv = (AVal *)(cmd + len);
	  argv[0].av_val = cmd;
	  argv[0].av_len = sizeof("rtmpdump")-1;
	  ptr += sprintf(ptr, "rtmpdump");
	  argc = 1;

	  argv[argc].av_val = ptr + 1;
	  argv[argc++].av_len = 2;
	  argv[argc].av_val = ptr + 5;
	  ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
	  argv[argc++].av_len = r->Link.tcUrl.av_len;

	  if (r->Link.app.av_val)
	    {
	      argv[argc].av_val = ptr + 1;
	      argv[argc++].av_len = 2;
	      argv[argc].av_val = ptr + 5;
	      ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val);
	      argv[argc++].av_len = r->Link.app.av_len;
	    }
	  if (r->Link.flashVer.av_val)
	    {
	      argv[argc].av_val = ptr + 1;
	      argv[argc++].av_len = 2;
	      argv[argc].av_val = ptr + 5;
	      ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val);
	      argv[argc++].av_len = r->Link.flashVer.av_len;
	    }
	  if (r->Link.swfUrl.av_val)
	    {
	      argv[argc].av_val = ptr + 1;
	      argv[argc++].av_len = 2;
	      argv[argc].av_val = ptr + 5;
	      ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
	      argv[argc++].av_len = r->Link.swfUrl.av_len;
	    }
	  if (r->Link.pageUrl.av_val)
	    {
	      argv[argc].av_val = ptr + 1;
	      argv[argc++].av_len = 2;
	      argv[argc].av_val = ptr + 5;
	      ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
	      argv[argc++].av_len = r->Link.pageUrl.av_len;
	    }
	  if (r->Link.extras.o_num) {
	    ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
	    AMF_Reset(&r->Link.extras);
	  }
	  argv[argc].av_val = ptr + 1;
	  argv[argc++].av_len = 2;
	  argv[argc].av_val = ptr + 5;
	  ptr += sprintf(ptr, " -y \"%.*s\"",
	    r->Link.playpath.av_len, r->Link.playpath.av_val);
	  argv[argc++].av_len = r->Link.playpath.av_len;

	  av = r->Link.playpath;
	  /* strip trailing URL parameters */
	  q = memchr(av.av_val, '?', av.av_len);
	  if (q)
	    {
	      if (q == av.av_val)
		{
		  av.av_val++;
		  av.av_len--;
		}
	      else
		{
		  av.av_len = q - av.av_val;
		}
	    }
	  /* strip leading slash components */
	  for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
	    if (*p == '/')
	      {
		p++;
		av.av_len -= p - av.av_val;
		av.av_val = p;
		break;
	      }
	  /* skip leading dot */
	  if (av.av_val[0] == '.')
	    {
	      av.av_val++;
	      av.av_len--;
	    }
	  file = malloc(av.av_len+5);

	  memcpy(file, av.av_val, av.av_len);
	  file[av.av_len] = '\0';
	  for (p=file; *p; p++)
	    if (*p == ':')
	      *p = '_';

	  /* Add extension if none present */
	  if (file[av.av_len - 4] != '.')
	    {
	      av.av_len += 4;
	    }
	  /* Always use flv extension, regardless of original */
	  if (strcmp(file+av.av_len-4, ".flv"))
	    {
	      strcpy(file+av.av_len-4, ".flv");
	    }
	  argv[argc].av_val = ptr + 1;
	  argv[argc++].av_len = 2;
	  argv[argc].av_val = file;
	  argv[argc].av_len = av.av_len;
	  ptr += sprintf(ptr, " -o %s", file);
	  now = RTMP_GetTime();
	  if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
	    {
	      printf("Duplicate request, skipping.\n");
	      free(file);
	    }
	  else
	    {
	      printf("\n%s\n\n", cmd);
	      fflush(stdout);
	      server->filetime = now;
	      free(server->filename.av_val);
	      server->filename = argv[argc++];
	      spawn_dumper(argc, argv, cmd);
	    }

	  free(cmd);
	}
      pc.m_body = server->connect;
      server->connect = NULL;
      RTMPPacket_Free(&pc);
      ret = 1;
	  RTMP_SendCtrl(r, 0, 1, 0);
	  SendPlayStart(r);
	  RTMP_SendCtrl(r, 1, 1, 0);
	  SendPlayStop(r);
    }
  AMF_Reset(&obj);
  return ret;
}