Play MPEG-2 TS using MseStreamSource - c#

I need to display a live video stream in a UWP application.
The video stream comes from a GoPro. It is transported by UDP messages. It is a MPEG-2 TS stream. I can play it successfully using FFPlay with the following command line :
ffplay -fflags nobuffer -f:v mpegts udp://:8554
I would like to play it with MediaPlayerElement without using a third party library.
According to the following page :
https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/supported-codecs
UWP should be able to play it. (I installed the "Microsoft DVD" application in the Windows Store).
I receive the MPEG-2 TS stream with a UdpClient. It works well.
I receive in each UdpReceiveResult a 12 bytes header, followed by 4, 5, 6, or 7 MPEGTS packets (each packet is 188 bytes, beginning with 0x47).
I created a MseStreamSource :
_mseStreamSource = new MseStreamSource();
_mseStreamSource.Opened += (_, __) =>
{
_mseSourceBuffer = _mseStreamSource.AddSourceBuffer("video/mp2t");
_mseSourceBuffer.Mode = MseAppendMode.Sequence;
};
_mediaPlayerElement.MediaSource = MediaSource.CreateFromMseStreamSource(_mseStreamSource);
This is how I send the messages to the MseStreamSource :
UdpReceiveResult receiveResult = await _udpClient.ReceiveAsync();
byte[] bytes = receiveResult.Buffer;
mseSourceBuffer.AppendBuffer(bytes.AsBuffer());
The MediaPlayerElement displays the message "video not supported or incorrect file name". (not sure of the message, my Windows is in French).
Is it a good idea to use the MseAppendMode.Sequence mode ?
What should I pass to the AppendBuffer method ? The raw udp message including the 12 bytes header or each MPEGTS 188 bytes packet ?

I finally got the video working !
Here are the steps I follow to extract the MPEG-TS packets and correctly send them to the MseStreamSource :
The MseSourceBuffer needs to be in "Sequence" mode :
_mseSourceBuffer.Mode = MseAppendMode.Sequence;
For each received UDP datagram, I extract the MPEG-TS packets. To do that, I ignore the first 12 bytes of the UDP datagram. Then I extract each 188 bytes packet in a separate array (each packet starts with 0x47).
I send each packet to a synchronized queue.
I dequeue the packets from the queue and send them grouped to the MseSourceBuffer. I create a new group for each PAT packet (pid = 0) :
byte[] bytes;
// [...] combine the packets of the group
mseSourceBuffer.AppendBuffer(bytes.AsBuffer());
I tried to use a MemoryStream and call the AppendStream() method, but with no success.
Also care about threads synchronization : packets order should not be lost. That is the reason for the synchronized queue.
Hope it can help someone else.
This wikipedia MPEG-TS page helped me a lot.

Related

Printing from Datecs FML 10KL

I am using .NET CF and my task is to print fiscal and non-fiscal receipt.
So I need to connect to the FML 10 KL via bluetooth.
I am using SerialPort to do this, but after sending commands nothing happens.
I tried sending the commands like this :
byte[] buf = new byte[218];
using (StreamWriter writer = new StreamWriter(inPort.BaseStream))
{
writer.Write(buf);
//inPort.NewLine = "\n";
//var msg = inPort.ReadLine();
}
I populate "buf" with my command.
After that I try to read the responce but everytime I get timeout.Also I tried to "write" with text and not byte array, but I get the same result.
If anyone can give me some advice that would be great.
Most serial devices including Bluetooth SPP and Socket controlled devices do not support Unicode. If you have a string, you need to use Encoding ASCII (or UTF8) getBytes to get a byte arry suitable for the serial or direct socket connection. If you do not care for this, you may get a byte sequence of {0x00, 0x41} (Unicode) for an 'A' instead of the needed {0x41} only.
If you try to print something, verify that the code you send is valid by writing the data to a file and send the file as it is using a terminal application (former HyperTerminal).
Most printers support a Dump mode. Verify that the code you build is transmitted un-altered to the printer by using the dump mode and compare with the validated code that you used to print.
The Operating System and the target device may use a buffer. Ensure the buffer is flushed and then close the Stream before disconnecting.
With the Serial Port class ensure the device and the class use the same parameters, ie both 8Bit etc. For Bluetooth SPP the baud rate is adopted, you may use 115200 or 57600 without failing.
I found a Class that supports the Datecs Fiscal Printing Protocol: https://github.com/wqweto/UcsFiscalPrinters/blob/master/Samples/Demo1/Program.cs you should probably use this or look at how the class does print on the Datecs.
I figured out my problem.It was the connection to the mobile printer.
I am connecting and writing like this:
SerialPort inport = new SerialPort("COM5", 115200, Parity.None, 8,
StopBits.One);
inport.Write(buf, 0, buf.Length);
Thread.Sleep(1000);
I need to use Thread.Sleep because the buffer is getting full and some of data is not getting printed.

Sending some constraints to client from server in C#

I have created a simple server using socket programming in C# which will receive a file from the client side. My sample code segment is given below.
I want to add some restrictions. I want to make a limit on the file size (such as 4 KB or 2 KB) and allowable file formats (such as .doc, .txt, .cpp, etc.) which will be sent to the client as soon as the client connects to the server so that the client can send files accordingly. How will I do that?
Sample code segment:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
namespace FileTransfer
{
class Program
{
static void Main(string[] args)
{
// Listen on port 1234
TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
Console.WriteLine("Server started");
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected to client");
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
//Will add some lines to add restrictions...
}
}
}
}
Which additional lines will I have to add to the code to send the restrictions to client?
Basically I think mainly you need two things:
define application protocol as suggested in other answer
and handle partial read/writes
For handling partial reads (not sure how much such function is needed for write) you may use function like below:
public static void ReadWholeArray (Stream stream, byte[] data)
{
int offset=0;
int remaining = data.Length;
while (remaining > 0)
{
int read = stream.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
Thing is traditional Stream.Read() doesn't guarantee to read as many bytes as you told it, this method on the other hand, will ensure to have read as many bytes as specified in data.Length parameter. So you can use such function to implement the desired application protocol instead.
Some relevant information about such application protocols you will find here too
Ok this is for example how the server could send file length limit and the file extension:
// Send string
string ext = ".txt";
byte [] textBytes = Encoding.ASCII.GetBytes(ext);
ns.Write(textBytes, 0, textBytes.Length);
// Now, send integer - the file length limit parameter
int limit = 333;
byte[] intBytes = BitConverter.GetBytes(limit);
ns.Write(intBytes, 0, intBytes.Length); // send integer - mind the endianness
But you will still need some kind of protocol otherwise you should let client read the "full" stream and parse these data later somehow, which isn't trivial if the data doesn't have fixed length etc - otherwise how will the client distinguish which part of the message is text, which integer?
You seem to be making the classical socket mistake. The given code and explanation seem to assume sockets handle in messages. They don't. When used this way, you're using streaming internet sockets, which provide a stream, not messages.
You don't show any code that does the actual sending, so I'm guessing that you just pump a file's data to the other side and close the connection. How else will you know you've successfully transferred an entire file?
This set of rules that client and server have to follow in order to usefully exchange data through sockets is called an application protocol. You will have to have one, otherwise you'll just be sending data to $deity knows where, and you'll have no control over it at all. This means server nor client will know what's going on, they'll just be sending and receiving data and hoping all goes well. So there's not "a few lines" you have to add to your code, you'll have to restructure it entirely.
There are many ways to define an application protocol and many options to choose from, so I'm going to show you an arbitrary one: a textual explanation of messages that are prefixed with an ID and a payload length (if applicable), both in unspecified numeric variables. You could choose little-endian four-byte unsigned integers, for example.
Messages in this format are known as "Type/Length/Value" or TLV.
So we define these messages:
ID Name Direction Description Payload
1 ServerHello Server -> Client The server sends this message None.
to every connecting client. Or maybe server or
protocol version.
2 MaxUpload Server -> Client Sent after the ServerHello. Maximum upload size
in bytes.
3 AllowedExts Server -> Client Allowed upload extensions, The allowed extensions.
comma-separated. Sent after
MaxUpload message.
10 IncomingFile Client -> Server There's a file coming. The file name.
11 FileUpload Client -> Server The file to upload. The file data.
Sent after IncomingFile.
Now all that's required is to implement this application protocol in server and client and you're done.
You also have to decide what to do if a client or server doesn't adhere to the prototol. It can for example send a message that you can't parse, an unknown message ID, a message length that you don't want to support, an out-of-order message (FileUpload before IncomingFile) or a message that isn't conform the messages sent earlier, like a client uploading a larger file than the server said it would accept or an invalid extension. You also have to think about "acknowledgement" or response messages, like the server telling the client "OK, go ahead, send the next message".
All in all, this is a very broad question and not answered easily. I tried to address that in my comment to your question, which got removed. So here you have your answer.
You can learn more about this on the web, for example Beej's Guide to Network Programming as linked to by Giorgi (be sure to read the entire guide) and Stephen Cleary's blog.

Problems with TcpListener reading data from socket in c#

I am having trouble reading large messages sent to a TcpListener from a TcpClient using BeginReceive / BeginSend in C#.
I have tried to append a four byte length header to my messages, but on occasion I will not receive it as the first packet which is causing problems.
For example, I sent a serialized object that contains the values [204, 22, 0, 0] as the first four bytes of the byte array in my BeginSend. What I receive on the server in the BeginReceive is [17, 0, 0, 0]. I have checked Wireshark when sending simple strings and the messages are going through, but there's a problem with my code. Another example, is when I send "A b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 0" to test I constantly receive "p q r . . . 8 9 0".
I thought that if packets were received out of order or if one was dropped, TCP would handle the resubmission of the lost packets and/or reorder them before sending them up. This would mean that my first four bytes should always contain my message size in the header. However, looking at the above examples this isn't the case, or it is and my code is messed up somewhere.
After the code below I just look if it's sending a particular command or type of object then responding based on what was received.
One I have the core functionality in place and get a better understanding of the issue I can begin to refactor, but this really has me at a stand still.
I've been banging my head against a wall for days trying to debug this. I have read several articles and questions about similar problems, but I haven't found a way to apply the suggested fixes to this particular instance.
Thanks in advance for your assistance with this.
The YahtzeeClient in the following code is just a wrapper around TcpClient with playerinformation.
private void ReceiveMessageCallback(IAsyncResult AR)
{
byte[] response = new byte[0];
byte[] header = new byte[4];
YahtzeeClient c = (YahtzeeClient)AR.AsyncState;
try
{
// If the client is connected
if (c.ClientSocket.Client.Connected)
{
int received = c.ClientSocket.Client.EndReceive(AR);
// If we didn't receive a message or a message has finished sending
// reset the messageSize to zero to prepare for the next message.
if (received == 0)
{
messageSize = 0;
// Do we need to do anything else here to prepare for a new message?
// Clean up buffers?
}
else
{
// Temporary buffer to trim any blanks from the message received.
byte[] tempBuff;
// Hacky way to track if this is the first message in a series of messages.
// If messageSize is currently set to 0 then get the new message size.
if (messageSize == 0)
{
tempBuff = new byte[received - 4];
// This will store the body of the message on the *first run only*.
byte[] body = new byte[received - 4];
// Only copy the first four bytes to get the length of the message.
Array.Copy(_buffer, header, 4);
// Copy the remainder of the message into the body.
Array.Copy(_buffer, 4, body, 0, received - 4);
messageSize = BitConverter.ToInt32(header, 0);
Array.Copy(body, tempBuff, body.Length);
}
else
{
// Since this isn't the first message containing the header packet
// we want to copy the entire contents of the byte array.
tempBuff = new byte[received];
Array.Copy(_buffer, tempBuff, received);
}
// MessageReceived will store the entire contents of all messages in this tranmission.
// If it is an new message then initialize the array.
if (messageReceived == null || messageReceived.Length == 0)
{
Array.Resize(ref messageReceived, 0);
}
// Store the message in the array.
messageReceived = AppendToArray(tempBuff, messageReceived);
if (messageReceived.Length < messageSize)
{
// Begin receiving again to get the rest of the packets for this stream.
c.ClientSocket.Client.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveMessageCallback, c);
// Break out of the function. We do not want to proceed until we have a complete transmission.
return;
}
// Send it to the console
string message = Encoding.UTF8.GetString(messageReceived);
**Marked as resolved. The solution was to wrap the message in a header and end of message terminator then modify the code to look for these indicators.
The reasoning behind the use of sockets was due to constraints on the project where web services were not an option.
You are having problems because you don't know the first message size and sometimes you are getting more, sometimes less, sometimes you getting some of what's left in cache...
An easy solution is to ALWAYS send the message size before the actual messages, something like:
[MYHEADER][32 Bit Integer][Message Content]
Lets supose that MYHEADER is ASCII, just an dummy identifier, in this case I would:
1: Try to receive 12 bytes to catch the entire header (MYHEADER + 32Bit Integer) and don't do anything until you do. After that, if the header identifier IS NOT MYHEADER, then I would assume that the message got corrupted and would have something like a reset in the connection.
2: After I confirmed that the header is ok, I would check the 32bit integer for the message size and allocate the necessary buffer. (You might want to limit the memory usage here, something like 6Mb max and if your messages go beyond this, add a index after the 32bit integer to specify the message part...)
3: Try to receive until the message size specified in the header.
You don't seem to have a good understanding of the fact that TCP is a stream of bytes without boundaries. For example your header reading will fail if you get less than 4 bytes in that read.
A very simple way to receive a length prefixed message is using BinaryReader:
var length = br.ReadInt32();
var data = br.ReadBytes(length);
That's all. Make yourself familiar with all the standard BCL IO classes.
Usually it is best not to use sockets at all. Use a higher level protocol. WCF is good.

C# Sending Hex string to tcp socket

I'm trying to send a hex string to a tcp socket. I have some problems with the format or conversion of this string, cause I'm not very sure what the format its using.
I've written a WindowsPhone app which is working fine based on Socket Class.
This app emulates request, that are normaly send from a desktop program to a device which hosts a webservice.
Via wireshark, I found out, that the webservice will accept an input stream (think its in hex) and returns a 2nd. hex stream which contains the data I need.
So the desktop app is sending a stream
and Wireshark shows when :
Data (8 bytes)
Data: 62ff03fff00574600
Length: 8
Now I've tried a lot to reproduce this stream. I thougt, it used to be a UTF8 string and converted this stream to this format. But every time I send it, is see in Wireshark the following output: 62c3bf03c3bf00574600
As far as i've investigated 62 = b but ff send always c3bf.
Does somebody know how to send this stream in the right format?
Cheers,
Jo
The socket transport shouldn't care, the content of a TCP packet is binary representing "whatever".
From the code you pointed to in the comments:
byte[] payload = Encoding.UTF8.GetBytes(data);
socketEventArg.SetBuffer(payload, 0, payload.Length);
...
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
At the end of the socket send/receive (data == response). If that isn't occurring you need to figure how where the problem is. The first step is to write some very simple code like so:
string source = "your problem text string";
byte[] encode = Encoding.UTF8.GetBytes(source);
target = Encoding.UTF8.GetString(encode, 0, encode.Length);
Debug.Assert(source == target);
If that works, then output the 'encode' array can check to make sure that is contained in the packet data where it is being send, then verify that that is what is being received. If you are sending the right data but receiving it corrupted you have serious problems ... I doubt you find that but if so write a very simple test program that sends and receives on the same machine (localhost) to see if it is repeatable.
If I had to guess I would say that the characters being encoded are not Unicode or that Win phone doesn't properly support it (Proper unicode support).
As long as you don't know the protocol / the encoding the server expects you can only replay the known messages, like the bytes you provided in your question.
Therefore you just define directly the byte array like this:
byte[] payload = new byte[] {0x62, 0xff, 0x03, 0xff, 0xf0, 0x05, 0x74, 0x60};
and send it over the socket like you did with the encoded string before. The server should now accept the message like it was sent by the client you sniffed.

How can a C# app easily communicate and transfer files on a network?

How can a C# app easily communicate with an instance of itself present on another computer, which is on the same network, and transfer files and data?
Assuming the computers on the network have fixed local IP addresses, and they each know each others IPs. Would there also be a way to communicate if the IPs are unknown? based on some discovery protocol?
I heard the "Bonjour" service from Apple was a good protocol. Can we communicate via it from our Windows apps? Or do you have to use "sockets". I'm primarily looking for libraries or sample code that can fulfill my need easily, I don't want to develop my own TCP-based protocol or anything hardcore!
You can use System.Net.Sockets class to communicate and it have a method for sending file Socket.SendFile.
Update:
this is a good example for file sharing and sending file from C# help
The great thing about files and sockets in C# is that they're both exposed as streams. Copying a large file from one stream to another is pretty simple:
byte[] data = new byte[1024];
while(true) {
int bytesRead = filestream.read(data,0,data.Length);
if (bytesRead==0) break;
netstream.write(data,0,bytesRead);
}
Then just close the socket when you're done.
If you want to send metadata (filenames, sizes) or don't want to close the connection, you need some sort of protocol to handle this. FTP uses two seperate sockets(one for metadata, one for data; this is called out-of-band communication). If you're on a LAN with no firewalls, that can be perfectly acceptable. On the other hand, if you want to do internet transfer, getting a single port open is a difficult-enough task, and two is unbearable. If you don't care too much about performance, you could encode the bytes in base64 encoding, which makes sure that they're within a certain byte range. With base64, you can seperate messages with newlines or other non-alphanumeric characters. Then in the first message include the filename, size, or whatever, then send the data as a second message, then send a "that's the whole file" message so the client knows it's done.
Another tactic for messages is using an escape sequence. For instance, take your bytestream and replace each instance of '\0' with '\0\0'. Now use '\0\1' to signal the end-of-message, which is guaranteed not to be contained in your data message. Decode the '\0\0' back to '\0' on the receiving end. This works well enough in C, but I find that, in practice, looping through each byte can be slower than reading whole buffers in C#.
The best way is to adopt some sort of adaptive-length protocol. For instance, send the data in chunks of a certain size (say 512 bytes). Before each chunk, send a 32bit int representing the size of the chunk via System.BitConverter. So messages look like this (english):
Here's 512 bytes:
[data]
Here's 512 bytes:
[data]
Here's 32 bytes:
[data]
Here's 4 bytes:
That was the whole file
The advantage here is that you can make the copy/read buffers work for you (reading 512 bytes at a time), meaning your throughput is limited by your network stack instead of your C# code. The client reads the fixed-length 32-bit int that lets it know the size of the buffer it should use for the next [data] segment.
Here's some code to write messages like that:
logger.logger.debug("Sending message of length " + length);
byte[] clength = System.BitConverter.GetBytes(buffer.Length);
plaintextStream.Write(clength,0,clength.Length);
plaintextStream.Write(buffer,0,buffer.Length);
plaintextStream.Flush();
And here's some code to read them:
byte[] intbuf = new byte[int_32_size];
int offset = 0;
while (offset < int_32_size)
{
int read = 0;
read = d.plaintextStream.Read(intbuf,offset,int_32_size - offset);
offset += read;
}
int msg_size = System.BitConverter.ToInt32(intbuf,0);
//allocate a new buffer to fill the message
byte[] msg_buffer = new byte[msg_size];
offset = 0;
while (offset < msg_size)
{
int read = 0;
read = d.plaintextStream.Read(msg_buffer,offset,msg_size - offset);
offset += read;
}
return msg_buffer;
For transferring the files / data, you can use the TcpClient/TcpListener classes, which is nice abstractions over the grittier socket functionality. Or, you could simply have the application as a HTTP server using the HttpListener class, if that is easier/more appropiate for your application.
For discovery, if you are able to have a central server; then you could have each client connect to the server at startup, to register itself and retrieve a list of other online clients and their IP's. Subsequent communication can then take place directly between the clients.
A variation of this scheme, is to let the central server act as a proxy, which all traffic between the clients flow through. This would be mostly helpful to overcome firewall or routing issues if the clients is not on the same network (so it's propably not needed for your scenario).
To copy files you may also want to look at File Synchronization Provider that is part of Microsoft Sync Framework. http://msdn.microsoft.com/en-us/sync/bb887623.

Categories