Indy, How to know size of buffer needed? - delphi

I have some problem when using Indy (idTCPServer component) to read data send by client, the data itself was a hex formatted so i can't use AThread.Connection.ReadLn(); for that...
Here my sample data sended by client
24 24 00 11 12 34 56 FF FF FF FF 50 00 8B 9B 0D 0A
or
24 24 00 13 12 34 56 FF FF FF FF 90 02 00 0A 8F D4 0D 0A
PS: it's in bytes hexadecimal (data length may vary depend on command, at maximum 160 bytes) which i can't get string representation since $00 translated to null (which mean i can't use ReadLn)
Here my sample code
procedure TfrmMain.IdTCPServerExecute(AThread: TIdPeerThread);
var
Msg : Array[0..255] of Byte;
begin
AThread.connection.ReadBuffer(Msg,SizeOf(Msg));
AThread.connection.WriteBuffer(Msg,MsgSize,true);
end;
this code will not work if client not send 255 byte data, while in my case data length can be vary, i have tried this, but no response outputted
procedure TfrmMain.IdTCPServerExecute(AThread: TIdPeerThread);
var
Msg : Array of Byte;
MsgSize : integer;
begin
MsgSize := AThread.connection.ReadInteger; //doesn't actually get packet length?
SetLength(Msg, MsgSize);
AThread.connection.ReadBuffer(Msg,MsgSize);
AThread.connection.WriteBuffer(Msg,MsgSize,true);
end;
so how exactly i can count how many byte data sended by client (packet length)? or could someone tell me the right code to read the data?

The simple answer is: you can't. TCP is a stream protocol, so there is no concept of a message. The data is received in chunks, whose sizes may (and will) differ from the actually sent buffers (the network stack is free to slice or merge the stream at will).
You may built a message protocol on top of TCP, e.g. by starting the transmission, and every consequent message, by a "size field" and then waiting only for the bytes needed; you still need to check the actual size received and re-reading the rest if applicable.
The point is: packet length in the TCP world has nothing to do with the length of the sent messages.
What TIdTCPConnection does behind all the Read-methods is:
reading all available data from the network stack, appending it into an internal input buffer and returning the requested N bytes from the beginning of the buffer, if available (waiting for next chunk, if not).

The 3rd and 4th bytes in the data you have shown specify the total size of the data being sent. You were close to try ReadInteger(), but the way you used it includes the 1st and 2nd bytes, which is wrong. Try this instead:
procedure TfrmMain.IdTCPServerExecute(AThread: TIdPeerThread);
var
Unknown: Smallint; // maybe a msg type?
DataSize: Smallint;
Data: Array of Byte;
begin
Unknown := AThread.Connection.ReadSmallInt;
DataSize := AThread.Connection.ReadSmallInt - 4;
SetLength(Data, DataSize);
AThread.Connection.ReadBuffer(Data[0], DataSize);
//...
end;

Related

Indy 10 UdpClient and Open Sound Control

To control a Behringer X32 audio mixer, I have to send an OSC message like /ch/01/mix/fader ,f .3 to move a fader to 30%. The mixer, per the OSC protocol, is expecting the .3 to come in as a 4 character string - in hex it's 3E 99 99 9A. So special characters are involved.
TIdUDPClient is given the characters for 3E 99 99 9A, but it sends out 3E 3F 3F 3F. Likewise .4 wants to be 3E CC CC CD but 3E 3F 3F 3F is sent.
When you get up to .5 and greater, things work again as the characters are below 3F. For example, .6 should be 3F 19 99 9A and goes out as 3F 19 3F 3F.
Evidently the Behringer is only looking at the first two characters there.
I am using Delphi Rio with the Indy 10 version distributed with it. I can create a module in Lazarus with Lnet that works fine. But my main application is in Delphi where I need this ability. As you can see, I've tried several different ways with the same non-working result.
How do I send the proper characters?
procedure TCPForm1.OSCSendMsg;
var
OutValueStr: String;
I: Integer;
J: Tbytes;
B1: TIdbytes;
begin
If Length(CommandStr) > 0 then begin
OscCommandStr := PadStr(CommandStr); //convert CommandStr to OSC string
If TypeStr='' then OscCommandStr := OscCommandStr+','+#0+#0+#0;
If Length(TypeStr) = 1 then begin
If TypeStr='i' then Begin // Parameter is an integer
I := swapendian(IValue); //change to big endian
OscCommandStr := OscCommandStr+','+TypeStr+#0+#0+IntToCharStr(I);
OutValueStr := IntToStr(IValue);
end;
If TypeStr='f' then Begin // Parameter is a float (real)
I := swapendian(PInteger(#FValue)^); //typecast & change to big endian
//I := htonl(PInteger(#FValue)^); //typecast & change to big endian
//J := MakeOSCFloat(FValue);
OscCommandStr := OscCommandStr+','+TypeStr+#0+#0+IntToCharStr(I);
//OscCommandStr := OscCommandStr+','+TypeStr+#0+#0+char(J[0])+char(J[1])+char(J[2])+char(J[3]);
OutValueStr := FloatToStr(FValue);
end;
end;
//IdUDPClient2.Send(OSCCommandStr,IndyTextEncoding_UTF8);
//IdUDPClient2.Send(OSCCommandStr);
B1 := toBytes(OSCCommandStr);
IdUDPClient2.SendBuffer(B1);
if loglevel>0 then logwrite('OSC= '+ hexstr(OSCCommandStr));
Wait(UDPtime);
// if loglevel>0 then logwrite('OSC '+ OSCCommandStr);
end;
end;
function TCPForm1.IntToCharStr(I : Integer) : String;
var
CharStr : String;
MyArray: array [0..3] of Byte;
J: Integer;
begin
For J :=0 to 3 do MyArray[J] := 0;
Move(I, MyArray, 4); //typeset conversion from integer to array of byte
CharStr := '';
For J :=0 to 3 do //convert array of byte to string
CharStr := CharStr+char(MyArray[J]);
IntToCharStr := CharStr;
end;
UPDATE:
The system would not let me add this as an answer, so...
Thank you, Remy. At least as far as the X32 software simulator is concerned, adding the 8 bit text encoding gives the correct response. I'll have to wait until tomorrow to test on the actual mixer in the theater. A byte array might be better if we had control of both ends of the communication. As it is, I can't change the X32 and it wants to get a padded string (in Hex: 2F 63 68 2F 30 31 2F 6D 69 78 2F 66 61 64 65 72 00 00 00 00 2C 66 00 00 3E CC CC CD) for the text string "/ch/01/mix/fader ,f .4". The documentation of messages the X32 responds to is a long table of similar messages with different parameters. e.g. "/ch/01/mix/mute on", "/bus/1/dyn/ratio ,i 2", etc. This is all in accordance with the Open Sound Control protocol.
As always, you are the definitive source of Indy wisdom, so, thank you. I'll edit this note after my results with the actual device.
UPDATE:
Confirmed that the addition of 8 bit text encoding to the Send command works with the X32. Cheers! A couple questions as a result of this:
Is one send construct preferred over the other?
Where should I have read/learned more about these details of Indy?
3F is the ASCII '?' character. You are seeing that character being sent when a Unicode character is encoded to a byte encoding that doesn't support that Unicode character. For example, Indy's default text encoding is US-ASCII unless you specify otherwise (via the GIdDefaultTextEncoding variable in the IdGlobal.pas unit, or via various class properties or method parameters), and US-ASCII does not support Unicode characters > U+007F.
It seems like you are dealing with a binary protocol, not a text protocol, so why are you using strings to create its messages? I would think byte arrays would make more sense.
At the very least, try using Indy's 8-bit text encoding (via the IndyTextEncoding_8Bit() function in the IdGlobal.pas unit) to convert Unicode characters U+0000..U+00FF to bytes 0x00..0xFF without data loss, eg:
B1 := ToBytes(OSCCommandStr, IndyTextEncoding_8Bit); // not ASCII or UTF8!
IdUDPClient2.SendBuffer(B1);
IdUDPClient2.Send(OSCCommandStr, IndyTextEncoding_8Bit); // not ASCII or UTF8!

What's the representation of a set of char in PASCAL?

They ask me to represet a set of char like into "map memory". What chars are in the set? The teacher told us to use ASCII code, into a set of 32 bytes.
A have this example, the set {'A', 'B', 'C'}
(The 7 comes from 0111)
= {00 00 00 00 00 00 00 00 70 00
00 00 00 00 00 00 00 00 00 00
00}
Sets in pascal can be represented in memory with one bit for every element; if the bit is 1, the element is present in the set.
A "set of char" is the set of ascii char, where each element has an ordinal value from 0 to 255 (it should be 127 for ascii, but often this set is extended up to a byte, so there are 256 different characters).
Hence a "set of char" is represented in memory as a block of 32 bytes which contain a total of 256 bits. The character "A" (upper case A) has an ordinal value of 65. The integer division of 65 by 8 (the number of bits a byte can hold) gives 8. So the bit representing "A" in the set resides in the byte number 8. 65 mod 8 gives 1, which is the second bit in that byte.
The byte number 8 will have the second bit ON for the character A (and the third bit for B, and the fourth for C). All the three characters together give the binary representation of 0000.1110 ($0E in hex).
To demonstrate this, I tried the following program with turbo pascal:
var
ms : set of char;
p : array[0..31] of byte absolute ms;
i : integer;
begin
ms := ['A'..'C'];
for i := 0 to 31 do begin
if i mod 8=0 then writeln;
write(i,'=',p[i],' ');
end;
writeln;
end.
The program prints the value of all 32 bytes in the set, thanks to the "absolute" keyword. Other versions of pascal can do it using different methods. Running the program gives this result:
0=0 1=0 2=0 3=0 4=0 5=0 6=0 7=0
8=14 9=0 10=0 11=0 12=0 13=0 14=0 15=0
16=0 17=0 18=0 19=0 20=0 21=0 22=0 23=0
24=0 25=0 26=0 27=0 28=0 29=0 30=0 31=0
where you see that the only byte different than 0 is the byte number 8, and it contains 14 ($0E in hex, 0000.1110). So, your guess (70) is wrong.
That said, I must add that nobody can state this is always true, because a set in pascal is implementation dependent; so your answer could also be right. The representation used by turbo pascal (on dos/windows) is the most logical one, but this does not exclude other possible representations.

How to see if stream does not contain a valid bitmap to convert.

How can I tell if a stream contains a picture or not? I am working with Delphi xe8 FMX developing an iOS application. I have a listbox and am loading pictures into the items.
I can do this:
if not Assigned(S) then
s:=TMemoryStream.Create;
if not Assigned(clHTTP) then
clHTTP := TIDHTTP.Create;
with clHTTP do
begin
clHTTP.HandleRedirects := True;
clHTTP.AllowCookies := True;
clHTTP.RedirectMaximum := 110000;
clHTTP.Get(someimageURL,s);
end;
s.Seek(0,soFromBeginning);
try
LItem.ItemData.Bitmap.LoadFromStream(s);
except
clHTTP.Get(DefaultImageURL,s);
s.Seek(0,soFromBeginning);
LItem.ItemData.Bitmap.LoadFromStream(s);
end;
s.Free;
clHTTP.Free;
I would prefer not to use a try-except block because it appears this causes loading of the bitmaps to be inconsistent. For example, I have to scroll the listbox items out of view, then back into view to see the pictures.
A quick way would be to check for the file signature. For instance, here are some common image format signatures:
PNG: 89 50 4E 47 0D 0A 1A 0A
JPEG: FF D8 FF E0
GIF87a: 47 49 46 38 37 61
GIF89a: 47 49 46 38 39 61
A comprehensive list can be found here: http://en.wikipedia.org/wiki/List_of_file_signatures
This approach will not prove that the rest of the stream is valid, but it is a good start and will allow you at least to reject obvious ringers.

Parsing variable byte length message

I'd like to parse a proprietary message with the following format. The messages are of variable length from 8bytes to 16 bytes. If the message is more than 8 bytes, it is put into multi packet message.
Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7-14 Byte15
Header Header Flags SlaveID Sequence Command Command Data Chksum
I send these messages to a device and expect a response from said device with similar format. I know what the messages are supposed to say, but I'm really struggling to find a away to parse the data in real time and display the information in somesort of GUI framework. Currently, I'm planning on dumping the data into a .txt/.csv file and post-processing with matlab.
I'm a hardware guy. This is not my strong suit.
Example:
Command, Reponse
0xAC 04 01 01 00 8C 01 01 chksum
0x04 AC 83 01 00 8C 01 01 0B D9 chksum
0x8c01 command to test power supply 0x01
Device echos the command and power supply it was checking and also returns the bit-shifted value of the power supply voltage 0x0BD9

FFmpeg can't decode H264 stream/frame data

Recently I had chance to work with two devices that are streaming the H264 through RTSP.
And I've ran into some problem trying to decompress this stream using FFmpeg library.
Every time the "avcodec_decode_video2" is called - FFmpeg just says something like:
[h264 # 00339220] no frame!
My raw H264 stream I frame data starts like this: "65 88 84 21 3F F8 F8 0D..."
(as far as I understand this 0x65 indicates that it's a IDR frame?)
Other frames for one device starts like: "41 9A 22 07 F3 4E 48 CC...."
and for other device - like this: "61 9A 25 C1 1C 45 62 39...."
Am I missing some frame data here?
Does FFmpeg needs to have some extra parameters set up?
I was expecting at least "00 00 00 01" bytes at the start for the frame data... but this is what I've got..
Ok, managed to make things working.
I needed to include the sequence (SPS) and picture parameter sets
(PPS) for my frame data before sending frame to the FFmpeg.
I needed to add 4 extra bytes "00 00 00 01" after SPS and PPS data.
Here is a little picture showing what I mean:
Bytes "65 88..." is where my original frame data begins.
This SPS and PPS information was not included in RTP packet. I'm using Live555 library for RTSP streaming, so I've used subsessions "fmtp_spropparametersets" function to get what I need. This information was Base64 encoded. (Sample: Something like this "Z0KAKNoC0EkQ,aM48gA==") Note that there are two "parameters" SPS and PPS seperated by "," and those parameters doesn't have a "00 00 00 01" included, so you need to add them.
Some code sample (I'm using Qt library here):
QByteArray ba = pSubSession->fmtp_spropparametersets();
QList<QByteArray> recordsList = ba.split(',');
for (int i = 0; i < recordsList.size(); i++)
{
mExtraData.append(char(0x00));
mExtraData.append(char(0x00));
mExtraData.append(char(0x00));
mExtraData.append(char(0x01));
mExtraData += QByteArray::fromBase64(recordsList.at(i));
}
Now for every frame I do something like this:
QByteArray ba = QByteArray(4, 0); // Prepare the "00 00 00 01"
ba[3] = 0x01;
mpTrackVideo->buffer.insert(0, mExtraData);
mpTrackVideo->buffer.insert(mExtraData.size(), ba);
Year ago I thought I had H264 stream support integrated in my project till I've had chance to test it with some other devices...
So you need to keep in mind that some devices might send SPS and PPS data for every I frame... and some might not!

Resources