⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unaicystreamer.pas

📁 Voice Commnucation Components for Delphi
💻 PAS
📖 第 1 页 / 共 3 页
字号:

// --  --
procedure unaIcyStreamProvider.handleStatus();
var
  dataPort: word;
begin
  case (status) of

    iss_disconnected: begin
      //
      f_status := iss_connecting;
      f_connStartMark := timeMark();
      //
      // start data connection
      dataPort := str2intUnsigned(port, 8000) + 1;
      f_socksId := f_socks.createConnection(host, int2str(dataPort), f_connId);
    end;

    iss_connecting: begin
      // check if we had timed out
      if (timeOut < timeElapsed32(f_connStartMark)) then
	askStop();
    end;

    iss_connected: begin
      //
    end;

  end;
end;

// --  --
function unaIcyStreamProvider.pushSongTitle(const title, url: string): HRESULT;
var
  len: unsigned;
  str: string;
  //
  socksIdPush: unsigned;
  connIdPush: unsigned;
begin
  result := HRESULT(-1);
  //
  // start HTTP-push connection
  socksIdPush := f_socks.createConnection(host, port, connIdPush);
  if (0 < socksIdPush) then begin
    try
      str := 'GET /admin.cgi?pass=' + url_encode(password) + '&mode=updinfo&song=' + url_encode(title) + '&url=' + url_encode(url) + ' HTTP/1.0'#10 +
	     'User-Agent: VC IcyStreamer (Mozilla Compatible)'#10#10;
      //
      len := length(str);
      if (len = sendDataTo(@str[1], length(str), socksIdPush, connIdPush)) then
	result := S_OK;
      //
    finally
      f_socks.closeThread(socksIdPush);
    end;
  end;  
end;

// --  --
procedure unaIcyStreamProvider.sendStreamMetadata();
var
  data: string;
begin
  data := 'icy-name:' + f_title + #10 +
	  'icy-genre:' + f_genre + #10 +
	  'icy-url:' + f_url + #10 +
	  'icy-irc:#none' + #10 +
	  'icy-icq:0' + #10 +
	  'icy-aim:N/A' + #10 +
	  'icy-pub:' + choice(f_allowPublishing, '1', '0') + #10 +
	  'icy-br:' + int2str(f_bitrate) + #10#10;
  //
  sendText(data);
end;

// --  --
procedure unaIcyStreamProvider.setPassword(const value: string);
begin
  assert('' <> trim(value), 'Password must not be empty');
  //
  f_password := trim(value) + #10;
end;

// --  --
procedure unaIcyStreamProvider.startIn();
begin
  f_passIsOK := true;
  //
  inherited;
end;

{ unaIcyStreamConsumer }

// --  --
procedure unaIcyStreamConsumer.afterConstruction();
begin
  inherited;
  //
  f_dataBuf := unaMemoryStream.create();
  f_dataBuf.maxCacheSize := 100;	// give this stream lots of buffers
end;

// --  --
procedure unaIcyStreamConsumer.beforeDestruction();
begin
  inherited;
  //
  freeAndNil(f_dataBuf);
end;

// --  --
procedure unaIcyStreamConsumer.checkMetadata(size: unsigned; ptext: pointer);
var
  text: string;

  function getValue(vstart: unsigned): string;
  var
    vend: unsigned;
  begin
    vend := vstart;
    //
    while ((vend < size) and (text[vend] <> '''')) do
      inc(vend);
    //
    if ((vend < size) and (text[vend] = '''')) then
      dec(vend);
    //
    result := copy(text, vstart, vend + 1 - vstart );
  end;

var
  vpos: int;
  title: string;
  url: string;
begin
  if (0 < size) then begin
    //
    setLength(text, size);
    move(pText^, text[1], size);
  end
  else
    text := '';
  //
  vpos := pos('streamtitle=''', lowerCase(text));
  if (0 < vpos) then
    title := getValue(vpos + length('streamtitle='''));
  //
  vpos := pos('streamurl=''', lowerCase(text));
  if (0 < vpos) then
    url := getValue(vpos + length('streamurl='''));
  //
  if ('' = title) then
    // try Ice 1.3 header
    title := getServerHeaderValue('x-audiocast-name');
  //
  if ('' = url) then
    // try Ice 1.3 header
    url := getServerHeaderValue('x-audiocast-url');
  //
  updateSongInfo(title, url);
end;

// --  --
procedure unaIcyStreamConsumer.checkMetadata();
begin
  checkMetaData(loadMetaDataFromBuf(), pChar(f_metaDataBuf));
end;

// --  --
procedure unaIcyStreamConsumer.dataAvail(data: pointer; size: unsigned);
begin
  if (assigned(f_onDA)) then
    f_onDA(self, data, size);
end;

// --  --
function unaIcyStreamConsumer.getServerHeaderValue(const key: string): string;
var
  vpos: int;
  vend: int;
  len: int;
begin
  result := '';
  len := length(f_header);
  //
  if (0 < len) then begin
    vpos := pos(lowerCase(key) + ':', lowerCase(f_header));
    if (0 < vpos) then begin
      //
      inc(vpos, length(key) + 1);
      vend := vpos;
      //
      while ((vend < len) and (f_header[vend] <> #13) and (f_header[vend] <> #10)) do
	inc(vend);
      //
      result := trim(copy(f_header, vpos, vend + 1 - vpos));
    end;
  end;
end;

// --  --
procedure unaIcyStreamConsumer.handleSocketEvent(event: unaSocketEvent; id, connId: unsigned; data: pointer; len: unsigned);
var
  text: pChar;
  eoh: int;
  blockSize: unsigned;
  dataLeft: int;
begin
  case (event) of

    // -- client --

    unaseClientConnect: begin
      //
      // check if we are waiting for channel connection
      if (status = iss_connecting) then begin
	//
	// send hello (GET / HTTP/1.0) to server
	sendHello(id, connId);
      end;
    end;

    unaseClientData: begin
      //
      // got some data from server
      if (0 < len) then begin
	//
	text := data;
	dataLeft := len;
	//
	if (
	     (status = iss_connecting) and ((1 = pos('ICY 200', text)) or (1 = pos('HTTP/1.0 200', text)) or (1 = pos('HTTP/1.1 200', text)))
	   ) then
	  f_status := iss_connected;
	//
	if (not f_headerIsDone) then begin
	  //
	  eoh := pos(#13#10#13#10, text);
	  if (0 < eoh) then
	    f_headerIsDone := true
	  else
	    eoh := len + 1;
	  //
	  f_header := f_header + copy(text, 1, eoh - 1);
	  //
	  inc(text, eoh + 3);
	  dec(dataLeft, eoh + 3);
	  //
	  if (f_headerIsDone) then begin
	    //
	    f_dataBuf.clear();
	    f_metaDataAlign := str2intInt(getServerHeaderValue('icy-metaint'), 0);
	    f_isMetaData := false;	// stream starts with audio data
	    //
	    checkMetadata();
	  end;
	end;
	//
	if (f_headerIsDone) then
	  //
	  while (0 < dataLeft) do begin
	    //
	    if (f_isMetaData) then begin                       
	      //
	      if (0 = f_metaDataSize) then begin
		// we are at the beginning of metadata
		f_metaDataSize := ord(text[0]) shl 4;
		inc(text);
		dec(dataLeft);
	      end;
	      //
	      if (int(f_dataBuf.getSize()) + dataLeft >= int(f_metaDataSize)) then begin
		//
		// all metadata is here
		if (0 < f_metaDataSize) then begin
		  // notify about metadata
		  //
		  // check if we can notify directly from text
		  if ((1 > f_dataBuf.getSize()) and (dataLeft >= int(f_metaDataSize))) then
		    // yes, we can
		    checkMetadata(f_metaDataSize, text)
		  else begin
		    // no, notify from buffer
		    blockSize := f_dataBuf.write(text, f_metaDataSize - f_dataBuf.getSize());
		    inc(text, blockSize);
		    dec(dataLeft, blockSize);
		    //
		    checkMetadata();
		  end;
		end;
		//
		// back to audio
		inc(text, f_metaDataSize);
		dec(dataLeft, f_metaDataSize);
		//
		f_isMetaData := false;
	      end
	      else begin
		// not all metadata is here, save what we got now into buffer
		blockSize := f_dataBuf.write(text, dataLeft);
		inc(text, blockSize);
		dec(dataLeft, blockSize);
	      end;
	    end
	    else begin
	      //
	      // check if we need to care about metadata
	      if (0 < f_metaDataAlign) then begin
		//
		if (f_dataBuf.getSize() + unsigned(dataLeft) >= f_metaDataAlign) then begin
		  // write rest of audio data into buffer
		  blockSize := f_dataBuf.write(text, f_metaDataAlign - f_dataBuf.getSize());
		  // notify audio data from buffer
		  notifyAudioFromBuf();
		  //
		  inc(text, blockSize);
		  dec(dataLeft, blockSize);
		  //
		  f_isMetaData := true;
		  f_metaDataSize := 0;
		end
		else begin
		  // store data for now
		  f_dataBuf.write(text, dataLeft);
		  dataLeft := 0;
		end;
	      end
	      else begin
		// simply notify audio data
		dataAvail(text, dataLeft);
		dataLeft := 0;
	      end;
	    end;
	    //
	  end;	// WHILE (0 < dataLeft) ...
	//
      end;
    end;

    unaseClientDisconnect,
    unaseThreadStartupError: begin
      // have to stop due to connection error/disconnection
      askStop();
    end;

  end;
end;

// --  --
procedure unaIcyStreamConsumer.handleStatus();
begin
  case (status) of

    iss_disconnected: begin
      //
      f_status := iss_connecting;
      f_connStartMark := timeMark();
      // start data connection
      f_socksId := f_socks.createConnection(host, port, f_connId);
    end;

    iss_connecting: begin
      // check if we had timed out
      if (timeOut < timeElapsed32(f_connStartMark)) then
	askStop();
    end;

    iss_connected: begin
      //
    end;

  end;
end;

// --  --
function unaIcyStreamConsumer.loadMetaDataFromBuf(): unsigned;
begin
  result := f_dataBuf.getSize();
  if (f_metaDataBufSize < result) then begin
    //
    f_metaDataBufSize := result;
    mrealloc(f_metaDataBuf, f_metaDataBufSize);
  end;
  //
  result := f_dataBuf.read(f_metaDataBuf, result);
end;

// --  --
procedure unaIcyStreamConsumer.notifyAudioFromBuf();
var
  size: unsigned;
begin
  //
  // WARNIGN! Since f_metaDataBuf can be modified by loadMetaDataFromBuf()
  // we have to store the result (size) locally. That is because passing the
  // return value directly as second parameter for dataAvail() may lead to
  // passing invalid value as first parameter
  //
  size := loadMetaDataFromBuf();
  //
  dataAvail(f_metaDataBuf, size);
end;

// --  --
procedure unaIcyStreamConsumer.sendHello(id, connId: int);
var
  hello: string;
  socksId: unsigned;
begin
  if (0 > id) then
    socksId := f_socksId
  else
    socksId := unsigned(id);
  //
  if (0 > connId) then
    connId := f_connId;
  //
  if ('' = trim(f_url)) then
    f_url := '/';
  //
  hello := 'GET ' + f_url + ' HTTP/1.0'#13#10 +
	   'Host:' + f_host + #13#10 +
	   'Accept:*/*'#13#10 +
	   'User-Agent:VC 2.5 Listener 1.0'#13#10 +
	   'Icy-Metadata:1'#13#10 +
	   #13#10;
  //
  sendDataTo(@hello[1], length(hello), socksId, connId);
end;

// --  --
procedure unaIcyStreamConsumer.startIn();
begin
  f_header := '';
  f_headerIsDone := false;
  f_songTitle := '';
  f_songUrl := '';
  //
  inherited;
end;

// --  --
procedure unaIcyStreamConsumer.startOut();
begin
  inherited;
  //
  mrealloc(f_metaDataBuf);
  f_metaDataBufSize := 0;
end;

// --  --
procedure unaIcyStreamConsumer.updateSongInfo(const title, url: string);
begin
  if ((trim(title) <> f_songTitle) or
      (trim(url)   <> f_songUrl)) then begin
    //
    f_songTitle := title;
    f_songUrl := url;
    //
    if (assigned(f_onSIU)) then
      f_onSIU(self, title, url);
  end;
end;


{ unaIcyServerClientConnection }

// --  --
procedure unaIcyServerClientConnection.afterConstruction();
begin

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -