Convert Rtsp Packet to bitmap in c# - c#

Hello Every on I have implemented SharpRtsp in c# https://github.com/ngraziano/SharpRTSP
I am receiving rtsp Packets and it has h264 format It has following method in H264Payload Class
private List<byte[]> Process_H264_RTP_Frame(List<byte[]> rtp_payloads)
{
Console.WriteLine("RTP Data comprised of " + rtp_payloads.Count + " rtp packets");
List<byte[]> nal_units = new List<byte[]>(); // Stores the NAL units for a Video Frame. May be more than one NAL unit in a video frame.
for (int payload_index = 0; payload_index < rtp_payloads.Count; payload_index++)
{
// Examine the first rtp_payload and the first byte (the NAL header)
int nal_header_f_bit = (rtp_payloads[payload_index][0] >> 7) & 0x01;
int nal_header_nri = (rtp_payloads[payload_index][0] >> 5) & 0x03;
int nal_header_type = (rtp_payloads[payload_index][0] >> 0) & 0x1F;
// If the Nal Header Type is in the range 1..23 this is a normal NAL (not fragmented)
// So write the NAL to the file
if (nal_header_type >= 1 && nal_header_type <= 23)
{
Console.WriteLine("Normal NAL");
norm++;
nal_units.Add(rtp_payloads[payload_index]);
}
// There are 4 types of Aggregation Packet (split over RTP payloads)
else if (nal_header_type == 24)
{
Console.WriteLine("Agg STAP-A");
stap_a++;
// RTP packet contains multiple NALs, each with a 16 bit header
// Read 16 byte size
// Read NAL
try
{
int ptr = 1; // start after the nal_header_type which was '24'
// if we have at least 2 more bytes (the 16 bit size) then consume more data
while (ptr + 2 < (rtp_payloads[payload_index].Length - 1))
{
int size = (rtp_payloads[payload_index][ptr] << 8) + (rtp_payloads[payload_index][ptr + 1] << 0);
ptr = ptr + 2;
byte[] nal = new byte[size];
System.Array.Copy(rtp_payloads[payload_index], ptr, nal, 0, size); // copy the NAL
nal_units.Add(nal); // Add to list of NALs for this RTP frame. Start Codes like 00 00 00 01 get added later
ptr = ptr + size;
}
}
catch
{
// do nothing
}
}
else if (nal_header_type == 25)
{
Console.WriteLine("Agg STAP-B not supported");
stap_b++;
}
else if (nal_header_type == 26)
{
Console.WriteLine("Agg MTAP16 not supported");
mtap16++;
}
else if (nal_header_type == 27)
{
Console.WriteLine("Agg MTAP24 not supported");
mtap24++;
}
else if (nal_header_type == 28)
{
Console.WriteLine("Frag FU-A");
fu_a++;
// Parse Fragmentation Unit Header
int fu_header_s = (rtp_payloads[payload_index][1] >> 7) & 0x01; // start marker
int fu_header_e = (rtp_payloads[payload_index][1] >> 6) & 0x01; // end marker
int fu_header_r = (rtp_payloads[payload_index][1] >> 5) & 0x01; // reserved. should be 0
int fu_header_type = (rtp_payloads[payload_index][1] >> 0) & 0x1F; // Original NAL unit header
Console.WriteLine("Frag FU-A s=" + fu_header_s + "e=" + fu_header_e);
// Check Start and End flags
if (fu_header_s == 1 && fu_header_e == 0)
{
// Start of Fragment.
// Initiise the fragmented_nal byte array
// Build the NAL header with the original F and NRI flags but use the the Type field from the fu_header_type
byte reconstructed_nal_type = (byte)((nal_header_f_bit << 7) + (nal_header_nri << 5) + fu_header_type);
// Empty the stream
fragmented_nal.SetLength(0);
// Add reconstructed_nal_type byte to the memory stream
fragmented_nal.WriteByte(reconstructed_nal_type);
// copy the rest of the RTP payload to the memory stream
fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
}
if (fu_header_s == 0 && fu_header_e == 0)
{
// Middle part of Fragment
// Append this payload to the fragmented_nal
// Data starts after the NAL Unit Type byte and the FU Header byte
fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
}
if (fu_header_s == 0 && fu_header_e == 1)
{
// End part of Fragment
// Append this payload to the fragmented_nal
// Data starts after the NAL Unit Type byte and the FU Header byte
fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
// Add the NAL to the array of NAL units
nal_units.Add(fragmented_nal.ToArray());
}
}
else if (nal_header_type == 29)
{
Console.WriteLine("Frag FU-B not supported");
fu_b++;
}
else
{
Console.WriteLine("Unknown NAL header " + nal_header_type + " not supported");
}
}
// Output some statistics
Console.WriteLine("Norm=" + norm + " ST-A=" + stap_a + " ST-B=" + stap_b + " M16=" + mtap16 + " M24=" + mtap24 + " FU-A=" + fu_a + " FU-B=" + fu_b);
// Output all the NALs that form one RTP Frame (one frame of video)
return nal_units;
}
}
I have triew and done reading but i have not found any way to convert H264 Frame into bitmap I just want it to save as bitmaps can any body help me o convert this data to bitmap?

The end of that function returns data in H264 format
// Output all the NALs that form one RTP Frame (one frame of video)
return nal_units;
To get a bitmap you need to pass the H264 data into a Video Decompressor. If you are not familiar with digital video, then think about .ZIP or .RAR or .7Z files.
You cannot read the contents of these files without the right software to convert the compressed data back to a usable format.
The same applies to video. H264 is a compressed data format.
You need to use a H264 decompression codec to confer the nal_units into a bitmap (or another video format called YUV).
Some people use ffmpeg for this decompression. Others pass the H264 data into an Operating System API (like VideoToolbox on the Mac or MediaCodec on Android or whatever Windows supports).
But you need these extra steps.

Related

NFC MIFARE Classic 1K cannot read or write

I've a NFC reader along with MIFARE Classic 1K card. I've a Visual C# winforms project. Right now I'm able to connect to the reader and detect the card and get it's UUID. The problem I'm facing is while writing and reading data. I searched a lot on internet, found some solution even tested the demo code provided with the SDK... nothing's working.
Let me describe the workflow and code I'm using for writing, authenticating a block, sending APDU and reading the block.
Following is the code for writing data to block 5.
String tmpStr = Text;
int indx;
if (authenticateBlock(Block))
{
ClearBuffers();
SendBuff[0] = 0xFF; // CLA
SendBuff[1] = 0xD6; // INS
SendBuff[2] = 0x00; // P1
SendBuff[3] = (byte)int.Parse(Block); // P2 : Starting Block No.
SendBuff[4] = (byte)int.Parse("16"); // P3 : Data length
SendBuff[5] = 0xFF;
SendBuff[6] = 0xFF;
SendBuff[7] = 0xFF;
SendBuff[8] = 0xFF;
SendBuff[9] = 0xFF;
SendBuff[10] = 0xFF;
for (indx = 0; indx <= (tmpStr).Length - 1; indx++)
{
SendBuff[indx + 5] = (byte)tmpStr[indx];
}
SendLen = SendBuff[4] + 5;
RecvLen = 0x02;
retCode = SendAPDUandDisplay(2);
if (retCode != Card.SCARD_S_SUCCESS)
{
MessageBox.Show("fail write");
}
else
{
MessageBox.Show("write success");
}
}
else
{
MessageBox.Show("FailAuthentication");
}
CloseCardConnection();
The function SendAPDUandDisplay is as below
private int SendAPDUandDisplay(int reqType)
{
int indx;
string tmpStr = "";
pioSendRequest.dwProtocol = Aprotocol;
pioSendRequest.cbPciLength = 8;
//Display Apdu In
for (indx = 0; indx <= SendLen - 1; indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", SendBuff[indx]);
}
retCode = Card.SCardTransmit(hCard, ref pioSendRequest, ref SendBuff[0],
SendLen, ref pioSendRequest, ref RecvBuff[0], ref RecvLen);
if (retCode != Card.SCARD_S_SUCCESS)
{
return retCode;
}
else
{
try
{
tmpStr = "";
switch (reqType)
{
case 0:
for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
if ((tmpStr).Trim() != "90 00")
{
//MessageBox.Show("Return bytes are not acceptable.");
return -202;
}
break;
case 1:
for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
{
tmpStr = tmpStr + string.Format("{0:X2}", RecvBuff[indx]);
}
if (tmpStr.Trim() != "90 00")
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
else
{
tmpStr = "ATR : ";
for (indx = 0; indx <= (RecvLen - 3); indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
}
break;
case 2:
for (indx = 0; indx <= (RecvLen - 1); indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
break;
}
}
catch (IndexOutOfRangeException)
{
return -200;
}
}
return retCode;
}
Function authenticateBlock is as following
private bool authenticateBlock(String block)
{
ClearBuffers();
/*SendBuff[0] = 0xFF; // CLA
SendBuff[2] = 0x00; // P1: same for all source types
SendBuff[1] = 0x82; // INS: for stored key input
SendBuff[3] = 0x00; // P2 : Memory location; P2: for stored key input
SendBuff[4] = 0x05; // P3: for stored key input
SendBuff[5] = 0x01; // Byte 1: version number
SendBuff[6] = 0x00; // Byte 2
SendBuff[7] = (byte)int.Parse(block); // Byte 3: sectore no. for stored key input
SendBuff[8] = 0x60; // Byte 4 : Key A for stored key input
SendBuff[9] = (byte)int.Parse("1"); // Byte 5 : Session key for non-volatile memory
*/
SendBuff[0] = 0xD4;
SendBuff[1] = 0x4A;
SendBuff[2] = 0x01;
SendBuff[3] = 0x00;
SendBuff[4] = (byte) int.Parse(block);
SendBuff[5] = 0xFF;
SendBuff[6] = 0xFF;
SendBuff[7] = 0xFF;
SendBuff[8] = 0xFF;
SendBuff[9] = 0xFF;
SendBuff[10] = 0xFF;
/*SendLen = 0x0A;
RecvLen = 0x02;*/
SendLen = 4;
RecvLen = 255;
retCode = SendAPDUandDisplay(2);
if (retCode != Card.SCARD_S_SUCCESS)
{
//MessageBox.Show("FAIL Authentication!");
return false;
}
return true;
}
One strange thing to notice here is that whatever values I set in sendBuff this function always returns true value and the write data code "The very first code block" returns write success message
But after executing the write data code when I read that very block "5" in my case, there is nothing present there. My read block code returns an empty string and when I try to double check if data was written and my faulty code couldn't read I use an external software to verify that was the value added or not, that software also does not show the data that I wrote and got that write success message.
Ok following is the code I'm using to read block 5.
public string readBlock(String Block)
{
string tmpStr = "";
int indx;
if (authenticateBlock(Block))
{
ClearBuffers();
/*
SendBuff[0] = 0xFF; // CLA
SendBuff[1] = 0xB0;// INS
SendBuff[2] = 0x00;// P1
SendBuff[3] = (byte)int.Parse(Block);// P2 : Block No.
SendBuff[4] = (byte)int.Parse("16");// Le
*/
SendBuff[0] = 0xD4;
SendBuff[1] = 0x40;
SendBuff[2] = 0x01;
SendBuff[3] = 0x30;
SendBuff[4] = byte.Parse(Block.ToString(), System.Globalization.NumberStyles.HexNumber);
SendBuff[5] = 0xFF;
SendBuff[6] = 0xFF;
SendBuff[7] = 0xFF;
SendBuff[8] = 0xFF;
SendBuff[9] = 0xFF;
SendBuff[10] = 0xFF;
//SendLen = 5;
//RecvLen = SendBuff[4] + 2;
SendLen = 5;
RecvLen = 255;
retCode = SendAPDUandDisplay(2);
if (retCode == -200)
{
return "outofrangeexception";
}
if (retCode == -202)
{
return "BytesNotAcceptable";
}
if (retCode != Card.SCARD_S_SUCCESS)
{
return "FailRead";
}
// Display data in text format
for (indx = 0; indx <= RecvLen - 1; indx++)
{
tmpStr = tmpStr + Convert.ToChar(RecvBuff[indx]);
}
return (tmpStr);
}
else
{
return "FailAuthentication";
}
}
Please Note that the read block method is called after checking that is a reader connected connected, if so then I call the
readblock method and it returns an empty string
I've tried several values as you would see in comments but nothing seems to help, it's been 3 long days and I'm still stuck here.
Can someone please help me figure where I'm doing it wrong and what values should I send in order to authenticate the block?
Please do me a favour that if anyone gets to knows the problem in my code or want to correctify the values I'm setting in sendBuff[] then please quote them in C# code so I can use exactly the solution you want me to implement
Any sincere help would be highly regarded, thanks in advance.
I have only experimented with mifare 1k, using Arduino.
In this instance, after detecting the card, and retrieving the UUID, It needs to select the card before reading/writing. Are you doing this select card step?
As its a S50 1K Classic the 'bytemap' is different and the process cycle while ostensibly the same you need to check that its a S50 before continuing by getting the ATR/ATS and parsing it to retrieve the switch setting. With contact its ATR, contactless ATS but is technically the same thing. Under PCSC this is asking for the readerchangestate when asking the reader is a card present, done before sending an APDU. You can also get other settings at the same time.
For MFS50 you need to perform a 'S'elect then a 'L'ogin using the sector key then read that sectors first three of 4 blocks but ignore most of the first sector block - the keys are in the fourth block along with other control bytes. The 'UID' is returned on 'S'elect, success or fail on 'L'ogin, data or 'E'rror on reading the blocks in the sector. For 14443 sector 0 the first block is occupied by the 'manufacturing' data which depends on the construct can have 4, 7 or 12 bytes as the UID with from a data point of view embedded CRC and check bytes, so cannot be used as a UID - it is a data map. Lights, C, ultralights, EV1's have a different 'bytemap' and may or may not have 'L'ogins at card or sector. These are 14443 types, there is also a 15693 type which has just data in the sectors. Some Chinese 14443 have writable manufacturing blocks, but usually its a mix of static, settings and OTP (once bit set cannot unset, used for authentication and NFC verification of size!).
Classic 1K: ident UID: 66908648, requires sector key log in (A read, B
read/write)
Sector 0:
6690864838880400468f76594d100612
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff078069ffffffffffff
...
Sector 15:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff0780bcffffffffffff
Mifare ultralight: UID 0489d802a44080, might require sector login but key held elsewhere.
Sector 0:
0489D8DD
02A44080
66480900
00010001
.
Sector 1:
00000000
00000000
00000000
000000EE
15693: UID D89A1F24500104E0
Sector 0:
50555955
Sector 1:
48485353
Sector 2:
59435300
Sector 3:
00000000
...
Sector 15:
00000000
So, get the ATR/ATS and work out what card you have, then deal with it accordingly. Oh, and use the belt, braces and a piece of string approach - after writing to a card read it again to compare the written to what is expected. 15693 require sector complete writes else nothing gets written in that sector.
Will that be Type 2 NFC/NDEF - there are other standards. Have cannibalized a Zebra printer to encode and print NTAG201's bullseyes on the fly.

can't extraxt RTP Payload from RTP Packet

Hi I wanted to proxy multicast video to unicast like udpxy does: http://www.udpxy.com, but in C#
since I could not find any suitable RTP library that I could use (they were eather too complex or I could't understand how to use them), I decided to port over one that udpxy uses rtp.c: https://github.com/pcherenkov/udpxy/blob/master/chipmunk/rtp.c
everything went fine (almost, as I didn't want to use pointers), until I wanted to translate RTP_Process to C#
RTP_Process in C
RTP_process( void** pbuf, size_t* len, int verify, FILE* log )
{
int rtp_padding = -1;
size_t front_skip = 0, back_skip = 0, pad_len = 0;
char* buf = NULL;
size_t pkt_len = 0;
assert( pbuf && len && log );
buf = *pbuf;
pkt_len = *len;
if( verify && !RTP_verify( buf, pkt_len, log ) )
return -1;
if( 0 != RTP_hdrlen( buf, pkt_len, &front_skip, log ) )
return -1;
rtp_padding = buf[0] & 0x20;
if( rtp_padding ) {
pad_len = buf[ pkt_len - 1 ];
}
back_skip += pad_len;
if( verify && (pkt_len < (front_skip + back_skip)) ) {
(void) tmfprintf( log, "RTP_process: invalid header "
"(skip [%lu] exceeds packet length [%lu])\n",
(u_long)(front_skip + back_skip), (u_long)pkt_len );
return -1;
}
/* adjust buffer/length to skip heading and padding */
/*
TRACE( (void)tmfprintf( log, "In: RTP buf=[%p] of [%lu] bytes, "
"fskip=[%ld], bskip=[%lu]\n",
(void*)buf, (u_long)pkt_len,
(u_long)front_skip, (u_long)back_skip ) );
*/
buf += front_skip;
pkt_len -= (front_skip + back_skip);
/*
TRACE( (void)tmfprintf( log, "Out RTP buf=[%p] of [%lu] bytes\n",
(void*)buf, (u_long)pkt_len ) );
*/
*pbuf = buf;
*len = pkt_len;
return 0;
}
RTP_Process in C#
public byte[] RTP_process(int verify)
{
/* process RTP package to retrieve the payload: set
* pbuf to the start of the payload area; set len to
* be equal payload's length
*
* #param pbuf address of pointer to beginning of RTP packet
* #param len pointer to RTP packet's length
* #param verify verify that it is an RTP packet if != 0
* #param log log file
*
* #return 0 if there was no error, -1 otherwise;
* set pbuf to point to beginning of payload and len
* be payload size in bytes
*/
int rtp_padding = -1;
int front_skip = 0, back_skip = 0, pad_len = 0;
int pkt_len = 0;
//assert(pbuf && len && log);
//buf = *pbuf;
pbuf = buf;
//pkt_len = *len;
len = pkt_len;
/*
if (verify != 1 && RTP_verify() != 1)
RTPOK = - 1;
if (0 != RTP_hdrlen(buf, pkt_len, front_skip)) //?????
RTPOK = - 1;
*/
rtp_padding = buf[0] & 0x20;
if (rtp_padding != -1) //???????
{
pad_len = buf[pkt_len - 1];
}
back_skip += pad_len;
if ((verify != -1) && (pkt_len < (front_skip + back_skip))) //???????
{
Console.WriteLine("RTP_process: invalid header (skip {0} exceeds packet length {1})\n", (long)(front_skip + back_skip), (long)pkt_len);
RTPOK = - 1;
}
/* adjust buffer/length to skip heading and padding */
/*
TRACE( (void)tmfprintf( log, "In: RTP buf=[%p] of [%lu] bytes, "
"fskip=[%ld], bskip=[%lu]\n",
(void*)buf, (u_long)pkt_len,
(u_long)front_skip, (u_long)back_skip ) );
*/
//buf += front_skip;
//pkt_len -= (front_skip + back_skip);
/*
TRACE( (void)tmfprintf( log, "Out RTP buf=[%p] of [%lu] bytes\n",
(void*)buf, (u_long)pkt_len ) );
*/
pbuf = buf;
len = pkt_len;
RTPOK = 0;
return pbuf;
}
here the problems started
1. buf += front_skip; complained that operator += cannot be applied to operands of type byte[] and int
then why did it work in RTP_Process in C and what is a C# equivalent of that
2. in
if (rtp_padding != -1) //???????
{
pad_len = buf[pkt_len - 1]; //There is an exeption trown: System.IndexOutOfRangeException: Index was outside the bounds of the array.
its clear that I interpreted and translated something the wrong way, but the onlything I would like to do is to get MPEG-TS frame out of RTP stream to then forward it to a TCP socket, so if anyone can suggest a better way of doing that I would love to hear it
Thanks for Anwsering and Best Regards
}
First, I suggest to read carefully RFC-3550, it has all information about RTP-packet structure (mostly you need Section #5: RTP Fixed Header and extensions).
Then you have to implement RTP_hdrlen to calculate RTP header size, it must return front_skip value as RTP header size including extensions. So, you don't have to use buf += front_skip;, RTP payload starts from byte buf[front_skip].
You have wrong packet length parameter here: int pkt_len = 0;, that's why the exception is thrown here pad_len = buf[pkt_len - 1];.

Strange packets arrive on C# websocket server from Chrome websocket client

I have a C# server side websocket code, and I am sending test data from Chrome (javascript websocket client).
Following this: https://stackoverflow.com/a/8125509/2508439, and some other link, I created my C# webserver (server side) decode function as following:
public String DecodeMessage(Byte[] bytes)
{
Console.WriteLine("+DecodeMessage+");
String incomingData = String.Empty;
Byte secondByte = bytes[1];
bool masked = (bytes[1] & 128) != 0;
//long dataLength = secondByte & 127;
dataLength = secondByte & 127;
//Int32 indexFirstMask = 2;
indexFirstMask = 2;
if (masked)
{
Console.WriteLine("Masked bit SET");
}
if (dataLength == 126)
{
indexFirstMask = 4;
dataLength = bytes[3] | bytes[2] << 8;
}
else if (dataLength == 127)
{
indexFirstMask = 10;
dataLength = bytes[9] | bytes[8] << 8 | bytes[7] << 16 | bytes[6] << 24 | bytes[5] << 32 |
bytes[4] << 40 | bytes[3] << 48 | bytes[2] << 56;
}
//IEnumerable<Byte> keys = bytes.Skip(indexFirstMask).Take(4);
keys = bytes.Skip(indexFirstMask).Take(4);
Int32 indexFirstDataByte = indexFirstMask + 4;
Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
Console.WriteLine("dataLength : " + dataLength + " ; bytes.Length : " + bytes.Length);
Int32 j = 0;
for (Int32 i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeMessage-");
return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}
public String DecodeRemainingMessage(Byte[] bytes, long bytesAlreadyRead)
{
Console.WriteLine("+DecodeRemainingMessage+");
String incomingData = String.Empty;
Int32 indexFirstDataByte = 0;
if ( indexFirstMask == 10 )//special case, what to do here?
{
indexFirstDataByte = 10;
}
Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
//Byte[] decoded = new Byte[bytes.Length];
Int32 j = 0;
for (Int32 i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeRemainingMessage-");
return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}
Simple packets (125 size or less arrive just fine).
In case of size about 125 and less than 65535, also arrive fine (kind of: there is some detail but I'm not going in that right now [*]).
Packets above 65535: the whole decode function goes crazy:
Only the first time packet is decoded properly, and after that, what ever data I receive is totally binary (unreadable), and after the first packet arrives, in consecutive packets:
if (dataLength == 126)
{
...
}
else if (dataLength == 127) ...
both conditions are never fulfilled, and dataLength is always less than 126, which is then decoded as (small) packet, and hence never reconstructed properly.
Can anyone highlight what I may be doing wrong?
Thanks
[*]=> data below 65535 length sometimes comes in more than two packets, which then behaves the same way as the larger packets, and packets after the first time this function is hit never gets reconstructed again properly.
edit 1:
#Marc
Based on your comment, I have put the 'masked bit check' in above function, and I can see it is always set to '1' (as expected since this is only server side code for now).
I am also parsing the control frame in a different function, and in this function, provided my code is correct, I may be getting lots of junk data.
To elaborate, please see these functions below:
whole logical code:
The enum:
public enum ControlFrame { NA=0, CloseConnection=1, Ping=2, Pong=4, Text=8, Binary=16, ContinueFrame =32, FinalFrame=64 };
The parse control frame function:
private int ParseControlFrame(byte controlFrame)
{
int rv = (int)ControlFrame.NA;
bool isFinalFrame = (controlFrame & 0x80) == 0x80 ;
byte opCode = (byte)((controlFrame & 0x0F));
if ( opCode >= 0x3 && opCode <= 0x7 ||
opCode >= 0xB && opCode <= 0xF )//special frame, ignore it
{
Console.WriteLine("Reserved Frame received");
return rv;
}
if (opCode == 0x8 || opCode == 0x0 || opCode == 0x1 || opCode == 0x2 || opCode == 0x9 || opCode == 0xA) //proceed furter
{
if (opCode == 0x0) //continue frame
{
rv |= (int)ControlFrame.ContinueFrame;
Console.WriteLine("Continue Frame received");
}
if (opCode == 0x1) //text frame
{
rv |= (int)ControlFrame.Text;
Console.WriteLine("Text Frame received");
}
if (opCode == 0x2) //binary frame
{
rv |= (int)ControlFrame.Binary;
Console.WriteLine("Binary frame received");
}
if (opCode == 0x8) //connection closed
{
rv |= (int)ControlFrame.CloseConnection;
Console.WriteLine("CloseConnection Frame received");
}
if (opCode == 0x9) //ping
{
rv |= (int)ControlFrame.Ping;
Console.WriteLine("PING received");
}
if (opCode == 0xA) //pong
{
rv |= (int)ControlFrame.Pong;
Console.WriteLine("PONG received");
}
}
else // invalid control bit, must close the connection
{
Console.WriteLine("invalid control frame received, must close connection");
rv = (int)ControlFrame.CloseConnection;
}
if (isFinalFrame) //Final frame ...
{
rv |= (int)ControlFrame.FinalFrame;
Console.WriteLine("Final frame received");
}
//else
//{
// rv |= (int)ControlFrame.ContinueFrame;
// Console.WriteLine("Continue frame received");
//}
return rv;
}
Logical flow (code snippet from actual):
if (stream.DataAvailable)
{
long bytesAlreadyRead = 0;
bool breakMain = false;
while (client.Available > 0 )
{
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
Console.WriteLine("if (stream.DataAvailable):\nclient.Available : " + client.Available +
" ; bytes.Length : " + bytes.Length);
//translate bytes of request to string
String data = Encoding.UTF8.GetString(bytes);
Console.WriteLine("Message received on: " + DateTime.Now);
if (bytesAlreadyRead == 0)
{
int controlFrame = ParseControlFrame(bytes[0]);
if (controlFrame == (int)ControlFrame.NA ||
(int)(controlFrame & (int)ControlFrame.Ping) > 0 ||
(int)(controlFrame & (int)ControlFrame.Pong) > 0) //ignore it
{
}
else
{
if ((int)(controlFrame & (int)ControlFrame.CloseConnection) > 0)
{
Console.WriteLine("Connection #" + c.Key + " Closed on: " + DateTime.Now);
tcpClients.Remove(c.Key);
breakMain = true;
break;
}
else
{
string result = c.Value.DecodeMessage(bytes);
File.WriteAllText("recvfile.txt", result);
}
}
}
else
{
string result = c.Value.DecodeRemainingMessage(bytes, bytesAlreadyRead);
File.AppendAllText("recvfile.txt", "\n");
File.AppendAllText("recvfile.txt", result);
}
bytesAlreadyRead += bytes.Length;
}
if ( breakMain == true )
{
break;
}
}
I don't get garbage but data is missed.
If I don't put this check, then, I start receiving garbage.
Based on Console.WriteLine output, I see something similar for data less than 65535:
Message received on: 12/29/2017 12:59:00 PM
Text Frame received
Final frame received
Masked bit SET
Message received on: 12/29/2017 12:59:12 PM
Text Frame received
Final frame received
Masked bit SET
For data above 65535:
Message received on: 12/29/2017 1:02:51 PM
Text Frame received
Final frame received
Masked bit SET
Message received on: 12/29/2017 1:02:51 PM
Reserved Frame received
i.e. Less than 65535, I am ok (most of the time).
With above 65535, things get strange.
When you mentioned:
I wonder if what is happening is that you're getting multiple messages in a single Read call (perfectly normal in TCP), consuming the first message, and incorrectly treating the entire bytes as consumed.
I never thought of this before, maybe I need to handle this somehow as well?
edit 2:
Based on your comment, i have modified the 'if (stream.DataAvailable)' logic, so it keeps on reading data in while loop until all data stored locally is actually flushed out.
So i may be close to solving it (thanks for your feedback), but the 2nd time DecodeMessage() function is called, it still decrypts in garbage (binary) data.
I am working on figuring it out!
Thanks
edit 3:
Ok, based on your suggestion, I have sorted out most of the logic. However, special case in 'DecodeRemainingMessage' function is what still remains a mystery. [I shifted some variables from local to class scope, hence they are commented out in functions]...
If I got it correct, I should not need to place any special condition here, but in that case, I still receive garbage.
Any pointers?
[sorry for the messy code, will update it once I get the right picture!]
Thanks
edit 4:
Following all your advises in comments/chat helped me get to the point where I updated decode logic greatly, and was still unable to get right data in case of above 65535 bytes. But when I tried final logic with Firefox, I got all the data properly! So many thanks to you, and, I still need to figure out how to handle buggy Chrome client! Thanks!!
Edit: your code assumes that the incoming frames are always masked. This is probably OK if your code is only ever a server, but you might want to check whether bytes[1] & 128 is set (masked) or clear (not masked). If it isn't masked: the header is 4 bytes shorter. You should be OK if this is only ever a server, as (from 5.2 in RFC6455):
All frames sent from client to server have this bit set to 1.
but: it would be good to double-check. You'd be amazed how many buggy clients there are in the wild that violate the specification.
--
The overall code looks fine, and is broadly comparable to what I have here. I can't see anything immediately wrong. This makes me suspect that the issue here is TCP streaming; it isn't obvious that you method is doing anything to report back what quantity of bytes should be logically consumed by this frame - i.e. the total header length plus the payload length. For comparison to my code, this would be out int headerLength and frame.PayloadLength combined.
I wonder if what is happening is that you're getting multiple messages in a single Read call (perfectly normal in TCP), consuming the first message, and incorrectly treating the entire bytes as consumed. This would mean that you start reading in the wrong place for the next frame header. A single Read invoke can return a fragment of one frame, exactly one frame, or more than one frame - the only thing it can't return is 0 bytes, unless the socket has closed.

WebSocket is not able to send large data

How can I send Large data to WebSocket in Java Script?
Using below code I am able to send data for 126 characters but not more then this. (Written in C#)
public static void SendData(string text)
{
foreach (SocketClient client in ClientList)
{
if (client.Client.Connected)
{
try
{
NetworkStream l_Stream = client.Client.GetStream();
List<byte> lb = new List<byte>();
lb = new List<byte>();
lb.Add(0x81);
int size = text.Length;
lb.Add((byte)size);
lb.AddRange(Encoding.UTF8.GetBytes(text));
l_Stream.Write(lb.ToArray(), 0, size + 2);
}
catch
{
CloseClient(client);
}
}
}
}
}
Can someone please help me? I tried to use so many things but none of them are working for me.
I am using Chrome 25 for the same.
This is how a websocket frame looks according to RFC 6455:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
You are setting the payload length of the websocket frame in byte 1 (the 2nd byte - first byte is byte 0). A byte can only have 256 states. But the first bit of byte 1 is used for the masking flag. So you can only represent values from 0 to 127.
When the payload length is larger than 125, you have to set byte 1 to 126 and put the length into byte 2 and 3 ("Extended payload length"). When your payload is even longer than 65535 bytes, you have to set byte 1 to 127 and put the payload length into the bytes 2-9 ("Extended payload length" and "Extended payload length continued"). When your payload length is even larger than 64bit (16 Exabyte, or about 16 million Terabyte), then... you should rather send a bunch of trucks filled with hard drives.
More details about that can be found in the official Websocket RFC.
You can send any size of data to WebSocket but large data will be fragmented.
Check this: https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5 and 5.4
and
pastebin.com/TxGZaHTY
and
http://buildnewgames.com/websockets/
for more info.
Referred from this blog. There could be a limit for single payload size to send and receive in the WebSocket server. For instance, in tomcat, by default it is 8192 bytes, checkout org.apache.tomcat.websocket.textBufferSize in tomcat's doc.
So, make sure if there is any payload size limit in your WebSocket server, if yes then receive it as partial payloads.
package GoodExample;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import sun.misc.BASE64Encoder;
/**
*
* #author
* Anders, Improved by Christopher Price
*/
public class WebRTCSignal {
public static final int MASK_SIZE = 4;
public static final int SINGLE_FRAME_UNMASKED = 0x81;
private ServerSocket serverSocket;
private Socket socket;
public WebRTCSignal() throws IOException {
serverSocket = new ServerSocket(1337);
connect();
}
private void connect() throws IOException {
System.out.println("Listening");
socket = serverSocket.accept();
System.out.println("Got connection");
if(handshake()) {
listenerThread();
}
}
private boolean handshake() throws IOException {
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
HashMap<String, String> keys = new HashMap<>();
String str;
//Reading client handshake
while (!(str = in.readLine()).equals("")) {
String[] s = str.split(": ");
System.out.println();
System.out.println(str);
if (s.length == 2) {
keys.put(s[0], s[1]);
}
}
//Do what you want with the keys here, we will just use "Sec-WebSocket-Key"
String hash;
try {
hash = new BASE64Encoder().encode(MessageDigest.getInstance("SHA- 1").digest((keys.get("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
return false;
}
//Write handshake response
out.write("HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: " + hash + "\r\n"
+ "Origin: http://face2fame.com\r\n"
+ "\r\n");
out.flush();
return true;
}
private byte[] readBytes(int numOfBytes) throws IOException {
byte[] b = new byte[numOfBytes];
socket.getInputStream().read(b);
return b;
}
public void sendMessage(byte[] msg) throws IOException {
System.out.println("Sending to client");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream());
baos.write(SINGLE_FRAME_UNMASKED);
baos.write(msg.length);
baos.write(msg);
baos.flush();
baos.close();
convertAndPrint(baos.toByteArray());
os.write(baos.toByteArray(), 0, baos.size());
os.flush();
}
public void listenerThread() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
while (true) {
System.out.println("Recieved from client: " + reiceveMessage());
System.out.println("Enter data to send");
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
t.start();
}
public String reiceveMessage() throws IOException {
byte[] buf = readBytes(2);
//dont use this byte[] buf2 = readBytes(4);
int extendedsize = 0;
System.out.println("Headers:");
if(!convertAndPrintHeader(buf)){ // This means we detected an extended message
String CaryOverDetectiona = new String("");
byte[] bufadder1 = buf.clone();
byte[] bufadder2 = readBytes(2);
byte[] array1and2 = new byte[bufadder1.length + bufadder2.length];
System.arraycopy(bufadder1, 0, array1and2, 0, bufadder1.length);
System.arraycopy(bufadder2, 0, array1and2, bufadder1.length, bufadder2.length);
for (byte b : array1and2) {
CaryOverDetectiona = (String.format("%02X ", b));
System.out.print(CaryOverDetectiona);
}
int i = ((array1and2[1] & 0xFF) - 0x80);
//int c = ((array1and2[2] & 0xFF) - 0x80);
//System.out.println(c+128);
int j = ((array1and2[3] & 0xFF) - 0x80)+2;
//System.out.println("The size of this uber message is" + j);
extendedsize = i+j;
System.out.println("Extended Size is" + extendedsize);
//System.exit(0);
}
//convertAndPrint(buf2);// Check out the byte sizes
int opcode = buf[0] & 0x0F;
if (opcode == 8) {
//Client want to close connection!
System.out.println("Client closed!");
socket.close();
System.exit(0);
return null;
} else {
int payloadSize = getSizeOfPayload(buf[1]);
if (extendedsize>=126){
payloadSize = extendedsize;}
System.out.println("Payloadsize: " + payloadSize);
buf = readBytes(MASK_SIZE + payloadSize);
System.out.println("Payload:");
convertAndPrint(buf);
buf = unMask(Arrays.copyOfRange(buf, 0, 4), Arrays.copyOfRange(buf, 4, buf.length));
String message = new String(buf);
return message;
}
}
private int getSizeOfPayload(byte b) {
//Must subtract 0x80 from masked frames
int a = b & 0xff;
//System.out.println("PAYLOAD SIZE INT" + a);
return ((b & 0xFF) - 0x80);
}
private byte[] unMask(byte[] mask, byte[] data) {
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] ^ mask[i % mask.length]);
}
return data;
}
private boolean convertAndPrintHeader(byte[] bytes) {
StringBuilder sb = new StringBuilder();
String CaryOverDetection = new String();
// We must test byte 2 specifically for this. In the next step we add length bytes perhaps?
//for(int i = 0; i < bytes.length; i++) {
//}
for (byte b : bytes) {
CaryOverDetection = (String.format("%02X ", b));
if (CaryOverDetection.contains("FE")){
return false;
}
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());
return true;
}
private void convertAndPrint(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());
}
public static void main(String[] args) throws IOException, InterruptedException, NoSuchAlgorithmException {
WebRTCSignal j = new WebRTCSignal();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.println("Write something to the client!");
j.sendMessage(br.readLine().getBytes());
}
}
}

I have a log file with RTP packets: now what?

I have a log file with RTP packets coming off of a black box device. I also have a corresponding SDP file (RTSP DESCRIBE) for that. I need to convert this file into some kind of playable video file. Can I pass these two files to FFMpeg or VLC or something else and have it mux that data into something playable?
As an alternate plan, I can loop through the individual packets in code and do something with each packet. However, it seems that there are existing libraries for parsing this data. And it seems to do it by hand would be asking for a large project. Is there some kind of video file format that is a pretty raw mix of SDP and RTP? Thanks for your time.
Is there a way for FFmpeg or VLC to open an SDP file and then get their input packets through STDIN?
I generally use C#, but I could use C if necessary.
Update 1: Here is my unworking code. I'm trying to get some kind of output to play with ffplay, but I haven't had any luck yet. It gives me invalid data errors. It does go over all the data correctly as far as I can tell. My output is nearly as big as my input (at about 4MB).
public class RtpPacket2
{
public byte VersionPXCC;
public byte MPT;
public ushort Sequence; // length?
public uint Timestamp;
public uint Ssrc;
public int Version { get { return VersionPXCC >> 6; } }
public bool Padding { get { return (VersionPXCC & 32) > 0; } }
public bool Extension { get { return (VersionPXCC & 16) > 0; } }
public int CsrcCount { get { return VersionPXCC & 0xf; } } // ItemCount
public bool Marker { get { return (MPT & 0x80) > 0; } }
public int PayloadType { get { return MPT & 0x7f; } } // PacketType
}
static void Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Usage: <input RTP file> <output 3GP file>");
return;
}
var inputFile = args[0];
var outputFile = args[1];
if(File.Exists(outputFile)) File.Delete(outputFile);
// FROM the SDP : fmtp 96 profile-level-id=4D0014;packetization-mode=0
var sps = Convert.FromBase64String("Z0LAHoiLUFge0IAAA4QAAK/IAQ=="); // BitConverter.ToString(sps) "67-42-C0-1E-88-8B-50-58-1E-D0-80-00-03-84-00-00-AF-C8-01" string
var pps = Convert.FromBase64String("aM44gA=="); // BitConverter.ToString(pps) "68-CE-38-80" string
var sep = new byte[] { 00, 00, 01 };
var packet = new RtpPacket2();
bool firstFrame = true;
using (var input = File.OpenRead(inputFile))
using (var reader = new BinaryReader(input))
using (var output = File.OpenWrite(outputFile))
{
//output.Write(header, 0, header.Length);
output.Write(sep, 0, sep.Length);
output.Write(sps, 0, sps.Length);
output.Write(sep, 0, sep.Length);
output.Write(pps, 0, pps.Length);
output.Write(sep, 0, sep.Length);
while (input.Position < input.Length)
{
var size = reader.ReadInt16();
packet.VersionPXCC = reader.ReadByte();
packet.MPT = reader.ReadByte();
packet.Sequence = reader.ReadUInt16();
packet.Timestamp = reader.ReadUInt32();
packet.Ssrc = reader.ReadUInt32();
if (packet.PayloadType == 96)
{
if (packet.CsrcCount > 0 || packet.Extension) throw new NotImplementedException();
var header0 = reader.ReadByte();
var header1 = reader.ReadByte();
var fragmentType = header0 & 0x1F; // should be 28 for video
if(fragmentType != 28) // 28 for video?
{
input.Position += size - 14;
continue;
}
var nalUnit = header0 & ~0x1F;
var nalType = header1 & 0x1F;
var start = (header1 & 0x80) > 0;
var end = (header1 & 0x40) > 0;
if(firstFrame)
{
output.Write(sep, 0, sep.Length);
output.WriteByte((byte)(nalUnit | fragmentType));
firstFrame = false;
}
for (int i = 0; i < size - 14; i++)
output.WriteByte(reader.ReadByte());
if (packet.Marker)
firstFrame = true;
}
else input.Position += size - 12;
}
}
}
http://www.bogotobogo.com/VideoStreaming/videostreaming_etc.php
Stay away from doing anything at the packet level because you may get bogged down in the details of how encoded streams are packetized.
look thru the above link. SDP / RTP / RTSP streaming are pretty involved protocols that usually do not work when you try to hook them up directly to players expecting to just open a local media file.
If you are handling streams and you want to save a file from the stream , you might want to google 'filesinks' with any of the big media projects ( ffmpeg, vlc, live555, openrtsp ) because those projects already have opensource fileSink implementations (C, C++).
For example, in live555, all of the codecs have fileSink implementations in ./live/liveMedia directory.
If you have a stream from an SDP source, you can handle each of the tracks (audio, video) with a fileSink for the codec used by that track. Then you can mux those tracks to a player or play the tracks independently.
Open the SDP file in vlc and then write a small app to re-play your RTP packets out to the network again so that VLC can receive them.
Make sure that you play them out to 127.0.0.1 and a port number that matches the SDP file.
VLC will wait until it receives some packets, then use VLC to save the media without transcoding into a MP4 or similar file format.

Categories

Resources