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

📄 unaicystreamer.pas

📁 Voice Commnucation Components for Delphi
💻 PAS
📖 第 1 页 / 共 3 页
字号:
  f_metadata := unaStringList.create();
  //
  inherited;
end;

// --  --
procedure unaIcyServerClientConnection.beforeDestruction();
begin
  inherited;
  //
  freeAndNil(f_metadata);
end;

// --  --
function unaIcyServerClientConnection.checkTimeout(timeout: unsigned): bool;
begin
  result := (2 <= verLevel) or (timeout > timeElapsed32(f_timeMark));
end;

// --  --
constructor unaIcyServerClientConnection.create(server: unaIcyServer; serverId, connId, metaDataAlign: unsigned);
begin
  f_server := server;
  f_serverId := serverId;
  f_connId := connId;
  f_metaDataAlign := metaDataAlign;
  //
  f_timeMark := timeMark();
  f_streamPos := 0;
  //
  inherited create();
end;

// --  --
procedure unaIcyServerClientConnection.write(data: pArray; len: unsigned);
var
  md: string;
  sz: unsigned;
  b: byte;
begin
  if (2 <= verLevel) then begin
    //
    if ((0 < f_metaDataAlign) and (f_metaDataAlign <= f_streamPos)) then begin
      //
      // write metadata
      if (0 < f_metadata.count) then begin
	//
	md := f_metadata.get(f_metadata.count - 1);
	f_metadata.clear();
      end
      else
	md := '';	// no metadata, but we still need to write "0"
      //
      sz := length(md);
      //
      if (0 = sz) then
	b := 0
      else
	b := ((sz - 1) and $FFFFFFF0) shr 4 + 1;
      //
      if (0 < sz) then
	md := adjust(md, b shl 4, ' ', false);
      //
      if (1 > b) then begin
	// simply send b as it is
	inc(f_server.f_servedBytes, f_server.f_socks.sendData(f_serverId, @b, 1, f_connId));
      end
      else begin
	//
	md := char(b) + md;
	//
	inc(f_server.f_servedBytes, f_server.f_socks.sendData(f_serverId, @md[1], 1 + b shl 4, f_connId));
      end;
      //
      f_streamPos := 0;	// reset offset
    end;
    //
    if (0 < f_metaDataAlign) then
      sz := min(f_metaDataAlign - f_streamPos, len)
    else
      sz := len;
    //
    inc(f_server.f_servedBytes, f_server.f_socks.sendData(f_serverId, data, sz, f_connId));
    //
    inc(f_streamPos, sz);	// shift the offset
    //
    if (sz < len) then begin
      //
      // still have some data in buffer, let's recorse
      write(@data[sz], len - sz);
    end;
  end;
end;


{ unaIcyServerClientList }

// --  --
constructor unaIcyServerClientList.create();
begin
  // simply tell the parent we are dealing with objects
  inherited create(true);
end;

// --  --
function unaIcyServerClientList.getId(item: pointer): int64;
begin
  if (nil <> item) then
    result := unaIcyServerClientConnection(item).f_connId
  else
    result := 0;
end;


{ unaIcyServer }

// --  --
procedure unaIcyServer.afterConstruction();
begin
  f_clients := unaIcyServerClientList.create();
  f_dataStream := unaMemoryStream.create();
  f_metaDataStream := unaMemoryStream.create();
  //
  inherited;
end;

// --  --
procedure unaIcyServer.beforeDestruction();
begin
  inherited;
  //
  f_dataBufSize := 0;
  mrealloc(f_dataBuf);
  //
  freeAndNil(f_clients);
  freeAndNil(f_dataStream);
  freeAndNil(f_metaDataStream);
end;

// --  --
constructor unaIcyServer.create(const port: string; maxClients, metaDataAlign: unsigned; bitrate: int; const special: string);
begin
  f_maxClients := min(maxClients, c_maxClientsPerServer);
  f_metaDataAlign := metaDataAlign;
  f_bitrate := bitrate;
  f_special := special;
  //
  inherited create('', port);
end;

// --  --
procedure unaIcyServer.handleSocketEvent(event: unaSocketEvent; id, connId: unsigned; data: pointer; len: unsigned);
var
  index: int;
begin
  //
  case (event) of

    // -- client --
    // do not care about client, we have only server here

    // -- server --

    unaseServerConnect: begin
      //
      if (f_clients.count < f_maxClients) then begin
	//
	// accept new client
	f_clients.add(unaIcyServerClientConnection.create(self, id, connId, f_metaDataAlign));
	//
	if (iss_connecting = status) then
	  f_status := iss_connected;
      end
      else
	// drop the client
	f_socks.removeConnection(id, connId);
    end;

    unaseServerData: begin
      //
      onNewClientData(connId, data, len);
    end;

    unaseServerDisconnect: begin
      //
      // drop the client
      index := f_clients.locateById(connId);
      //
      if (0 <= index) then
	f_clients.removeByIndex(index);
      //
      if ((1 > f_clients.count) and (iss_connected = status)) then
	f_status := iss_connecting;	// return to sleepy mode
    end;

    // thread
    unaseThreadStartupError: begin
      //
      askStop();
    end;

  end;
end;

{

Possible server replies:

  ICY 401 Service Unavailable
  icy-notice1:<BR>SHOUTcast Distributed Network Audio Server/win32 v1.9.2<BR>
  icy-notice2:The resource requested is currently unavailable<BR>


  ICY 200 OK
  icy-notice1:<BR>This stream requires <a href="http://www.winamp.com/">Winamp</a><BR>
  icy-notice2:SHOUTcast Distributed Network Audio Server/win32 v1.9.2<BR>
  icy-name:VC 2.5 Streamer
  icy-genre:Rock2
  icy-url:http://lakeofsoft.com/vc/
  Content-Type:audio/mpeg
  icy-pub:0
  icy-metaint:8192
  icy-br:96


Metadata example:

  StreamTitle='';StreamUrl='';

}

// --  --
procedure unaIcyServer.handleStatus();
var
  i: unsigned;
  sz: unsigned;
begin
  //
  case (status) of

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

    iss_connecting: begin
      //
    end;

    iss_connected: begin
      //
      // check if we have metadata to send
      sz := f_metaDataStream.getFirstChunkSize();
      //
      if ((0 < sz) and (0 < f_clients.count)) then begin
	//
	setLength(f_metaDataBuf, sz);
	sz := f_metaDataStream.read(@f_metaDataBuf[1], sz);
	//
	if ((0 < sz) and lockNonEmptyList(f_clients, 100)) then begin
	  //
	  try
	    //
	    for i := 0 to f_clients.count - 1 do begin
	      //
	      unaIcyServerClientConnection(f_clients[i]).metadata.add(f_metaDataBuf);
	    end;
	  finally
	    f_clients.unlock();
	  end;
	end;
	//
      end;
      //
      sz := f_dataStream.getSize();
      //
      if ((f_metaDataAlign < sz) and (0 < f_clients.count)) then begin
	//
	if (sz > f_dataBufSize) then begin
	  //
	  f_dataBufSize := sz;
	  mrealloc(f_dataBuf, f_dataBufSize);
	end;
	//
	sz := f_dataStream.read(f_dataBuf, sz);
	//
	if ((0 < sz) and lockNonEmptyList(f_clients, 100)) then begin
	  //
	  try
	    //
	    for i := 0 to f_clients.count - 1 do begin
	      //
	      with (unaIcyServerClientConnection(f_clients[i])) do begin
		//
		if (checkTimeout(5000)) then begin	// give client 5 sec to report player ID
		  //
		  write(f_dataBuf, sz);
		end
		else begin
		  // drop this client
		  if (f_clientsToDropCount < high(f_clientsToDrop)) then begin
		    //
		    f_clientsToDrop[f_clientsToDropCount] := connId;
		    inc(f_clientsToDropCount);
		  end;
		end;
		//
	      end;
	      //
	    end;
	  finally
	    f_clients.unlock();
	  end;
	end;
	//
      end;
      //
      //
      if (0 < f_clientsToDropCount) then begin
	//
	for i := 0 to f_clientsToDropCount - 1 do
	  //
	  f_socks.removeConnection(f_socksId, f_clientsToDrop[i]);
	//
	f_clientsToDropCount := 0;
      end;
    end;

  end;
end;

// --  --
procedure unaIcyServer.onNewClientData(connId: unsigned; data: pArray; len: unsigned);
var
  index: int;
  hello: string;
  p: int;
  client: unaIcyServerClientConnection;
label
  levm1, lev1, lev2;
begin
  index := f_clients.locateById(connId);
  //
  if (0 <= index) then begin
    //
    client := unaIcyServerClientConnection(f_clients[index]);
    with (client) do begin
      //
	{
	  possible client hello:

	  GET / HTTP/1.0
	  Host:192.168.1.1
	  Accept:*/*
	  User-Agent:VC 2.5 Listener 1.0
	  Icy-Metadata:1
	  #13#10

	}
      if (2 > verLevel) then
	// add tcp data to client request header
	header := header + pChar(data);
      //
      case (verLevel) of

	-1: begin
  levm1:
	  //
	  // send goodbye to client
	  hello := 'ICY 401 Service Unavailable'#13#10 +
		   ''#13#10;
	  //
	  inc(f_servedBytes, f_socks.sendData(f_socksId, @hello[1], length(hello), connId));
	  //
	end;

	0: begin
	  //
	  if (1 = pos('GET /', header)) then begin
	    //
	    verLevel := 1;
	    goto lev1;
	  end;
	end;

	1: begin
  lev1:
	  //
	  p := pos('User-Agent:', header);
	  //
	  if (0 < p) then begin
	    //
	    if (('' = f_special) or (0 < pos(f_special, pChar(@header[p])))) then begin
	      //
	      verLevel := 2;
	      goto lev2;
	    end
	    else begin
	      // check if all header is here
	      if (0 < pos(#13#10#13#10, header)) then begin
		//
		// say goodbye to client
		verLevel := -1;
		goto levm1;
	      end;
	    end;
	    //
	  end;
	end;

	2: begin
  lev2:
	  //
	  verLevel := 3;	// do not send hello again
	  //
	  // send hello to client
	  hello := 'ICY 200 OK'#13#10 +
		   'icy-notice1:<BR>This stream requires Winamp or compatible player<BR>'#13#10 +
		   'icy-notice2:SHOUTcast compatible Audio Server v1.0 (c) Lake of Soft, Ltd<BR>'#13#10 +
		   'icy-name:Live Streamer 1.0'#13#10 +
		   'icy-genre:Rock'#13#10 +
		   'icy-url:http://lakeofsoft.com/vc/'#13#10 +
		   'Content-Type:audio/mpeg'#13#10 +
		   'icy-pub:0'#13#10 +
		   'icy-metaint:' + int2str(f_metaDataAlign) + #13#10 +
		   'icy-br:' + int2str(f_bitrate) + #13#10#13#10;
	  //
	  inc(f_servedBytes, f_socks.sendData(f_socksId, @hello[1], length(hello), connId));
	end;

      end;  // case
      //
    end;  // with
  end;
end;

// --  --
function unaIcyServer.write(data: pArray; len: unsigned): unsigned;
begin
  result := 0;
  //
  if (0 < f_clients.count) then begin
    //
    result := f_dataStream.write(data, len);
    //
    wakeUp();
  end
  else
    f_dataStream.clear();
end;

// --  --
function unaIcyServer.writeMetadata(const title, url: string): unsigned;
var
  data: string;
begin
  data := 'StreamTitle=''' + title + ''';StreamUrl=''' + url + '''';
  //
  result := f_metaDataStream.write(@data[1], length(data));
  //
  wakeUp();
end;


end.


⌨️ 快捷键说明

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