I am connection to the camera using live555 testRTSPClient application (http://www.live555.com/liveMedia/#testProgs)
./testRTSPClient rtsp://....
but it does not get any video stream. The problem seems to be that the camera tells in its SDP that it has two connections
...
s=/videoinput_1:0/h264_1/media.stm
c=IN IP4 0.0.0.0
m=video 11800 RTP/AVP 96
c=IN IP4 239.0.3.180/1
...
and testRTSPClient selects the last multicast one. In the Setup command the testRTSPClient tells following
...
User-Agent: ./testRTSPClient (LIVE555 Streaming Media v2017.06.04)
Transport: RTP/AVP;multicast;port=11800-11801
...
When connecting to another camera, which SDP contains only one connection (c=IN IP4 0.0.0.0) then everything is fine.
1) So the first question is that is it possible to force the testRTSPClient to select the UDP unicast instead? The ffplay streams from the camera nicely and Wireshark show that ffplay setups the transports with UDP unicast and not multicast.
2) Secondly, I am using my own C++ example similar to testRTSPClient. In the subSession setup I use the RTSPClient::sendSetupCommand function
rtspClient->sendSetupCommand(*subsession, continueAfterSetup, False, False, False);
but my program still does the Setup with multicast like the testRTSPClient. The parameter forceMulticastOnUnspecified does not seem to make any difference here. Currently, I see that the only option is to remove the second multicast connection substring from the SDP.
void continueAfterDescribe(RTSPClient* rtspClient, int resultCode, char* resultString)
{
...
char* const sdpDescription = resultString;
env << "Got a SDP description:\n" << sdpDescription << "\n";
// Hypothetical new code
if (user determined transport == UDP unicast)
removeSecondConnection(sdpDescription);
// Create a media session object from this SDP description:
MediaSession::createNew(env, sdpDescription);
...
But it is a hack in my mind. Is there any other option to select the unicast using live555 C++ API? I know that there is a TCP unicast option, but I am not interested in it for now.
Related
I have a problem to connect 2 computers by using UDP protocol. As reference I've used broadcast server and broadcast receiver provided by qt (version 5.9). So what's the problem. When I want to check this 2 programs on one computer, they works correct, but when i use them on the different machines, all crashes. Datagrams wasn't received by receiver computer. What am i do wrong? Could anyone answer me.
sender code:
void Sender::broadcastDatagram()
{
statusLabel->setText(tr("Now broadcasting datagram %1").arg(messageNo));
//! [1]
QByteArray datagram = "Broadcast message " + QByteArray::number(messageNo);
udpSocket->writeDatagram(datagram.data(), datagram.size(),
QHostAddress::Broadcast, 45454);
//! [1]
++messageNo;
}
`
and receiver
udpSocket->bind(QHostAddress::Any,45454, QUdpSocket::ShareAddress);
sender ip is 127.0.0.1 ;
receiver ip is 10.0.0.10
This is a simple problem, but I can't seem to figure out what I am doing wrong. I am attempting to read data sent to a port on a client using Boost and I have the following code which sets up 1) the UDP client, 2) a buffer for reading to and 3) an attempt to read from the socket:
// Set up the socket to read UDP packets on port 10114
boost::asio::io_service io_service;
udp::endpoint endpoint_(udp::v4(), 10114);
udp::socket socket(io_service, endpoint_);
// Data coming across will be 8 bytes per packet
boost::array<char, 8> recv_buf;
// Read data available from port
size_t len = socket.receive_from(
boost::asio::buffer(recv_buf,8), endpoint_);
cout.write(recv_buf.data(), len);
The problem is that the recieve_from function never returns. The server is running on another computer and generating data continuously. I can see traffic on this port on the local computer using Wireshark. So, what am I doing wrong here?
So, it turns out that I need to listen on that port for connections coming from anywhere. As such, the endpoint needs to be setup as
boost::asio::ip::udp::endpoint endpoint_(boost::asio::ip::address::from_string("0.0.0.0"), 10114);
Using this setup, I get the data back that I expect. And fyi, 0.0.0.0 is the same as INADDR_ANY.
I have a UDP server using the following code:
void initialize()
{
connect(&_udpSocket, SIGNAL(readyRead()), this, SLOT(onUdpDatagram()));
_udpSocket.bind(QHostAddress::Any, 28283);
}
void onUdpDatagram()
{
qDebug() << "udp packet received!";
_udpSocket.write("Hello");
}
Unfortunately when a UDP packet is received, I have the following error in the log:
QIODevice::write: device not open
How can I make the UDP socket writable? I tried to create another socket for the answer that connect to the sender address and port but the sending won't use the 28283 port anymore...
Any idea?
For info: I'm using Qt 5.2.1 on MacOS 10.9
UDP is not a connection-based protocol. You don't get a separate socket for each peer, instead there's one socket for all communication on a single port.
Therefore, there's some extra effort needed to reply to an incoming UDP packet. You need to retrieve the sender address from the datagram you received, and send back to that same address. In the sockets API this is done by using recvfrom and sendto functions instead of recv (or read) and send (or write) -- the latter are designed for connected sockets like you use with TCP.
You didn't show the declaration (really, the type) for your _udpSocket variable, so I'm assuming that you are using a QUdpSocket. In that case, it looks like you will want to use the readDatagram and writeDatagram functions, which like recvfrom and sendto, have an additional parameter for the peer address (actually, it's a pair, one for the IP address, one for the port).
Here's what the Qt documentation says about that:
The most common way to use this class is to bind to an address and port using bind(), then call writeDatagram() and readDatagram() to transfer data. If you want to use the standard QIODevice functions read(), readLine(), write(), etc., you must first connect the socket directly to a peer by calling connectToHost().
Coincidentally, this warning was introduced by me in Qt upstream:
QIODevice::write: device not open
It should be pretty clear unlike before the introduction of this, namely: you have forgotten to connect to the host with your udp socket. You cannot expect it to write and/or read if it is not even open and/or connected. See the documentation for details:
If you want to use the standard QIODevice functions read(), readLine(), write(), etc., you must first connect the socket directly to a peer by calling connectToHost().
You have to do something like this somewhere in your code:
_udpSocket.connectToHost(myHostAddress, 28283, ReadWrite, AnyIPProtocol);
The last two parameters can be skipped as they are the default. As you can read from the documentation, this method call will open the socket for you, too, which is necessary to get done for QIODevice read and write operations.
That being said, you really should not neglect error checking in your code as it currently seems to stand. It will be difficult to find the issues this way.
Also, it is ice on the cake, but I would encourage you to start using the "new" signal-slot syntax, which is not so new, but much more modern and handier:
void initialize()
{
connect(&_udpSocket, &QUdpSocket::connected, [&_udpSocket]() {
connect(&_udpSocket, &QUdpSocket::readyRead, [&_udpSocket]() {
qDebug() << "udp packet received!";
if (_udpSocket.write("Hello") != 6)
qDebug() << "Failed to write:" << _udpSocket.errorString();
});
});
connect(&_udpSocket, &QUdpSocket::error, [&_udpSocket]() {
qDebug() << "Error occured:" << _udpSocket.errorString();
});
_udpSocket.connectToHost(myHostAddress, 28283, ReadWrite, AnyIPProtocol);
}
I'm writing an application on an embedded device which receives an RTP stream which carries G.729, PCM or H.264. The packets arrive to my application as a char* to the RTP packet. I would like to be able to see or listen to the stream (as a test), but on this device I don't have player. I thought I may forward this stream to a socket and play the RTP stream somewhere else, like on a Linux machine running a player. Would this be possible? I don't have RTSP, only RTP. Is VLC, for instance, a possible way to do this? Can I simply send the RTP packets to the socket to play them on the other side?
Thanks!
example of SDP that contains H.264 stream:
Server: rtsp server
Content-type: application/sdp
Content-base: rtsp://[some URL]
Content-length: 505
v=0
o=rtsp 1295996924 1590699491 IN IP4 0.0.0.0
s=RTSP Session
i=rtsp server
c=IN IP4 192.168.1.2
t=0 0
a=control:*
m=audio 0 RTP/AVP 97
a=rtpmap: 97 mpeg4-generic/8000/1
a=fmtp: 97 streamtype=5; profile-level-id=15; objectType=2; mode=AAC-hbr;
a=range:npt=now-
a=control:trackID=0
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=42E015; sprop-parameter-sets=Z0LgFdoHgtE=,aM4wpIA=; packetization-mode=1
a=range:npt=now-
a=framesize:96 480-352
a=control:trackID=1
no, you cannot. simple RTP doesn't contain any info about the stream format etc., only info about the packet itself: sequence number, timestamp, additional synchronization info. the simplest way to stream RTP is RTP/MPEG TS (MPEG Transport Stream).
unfortunately I don't know ready to use solution. VLC can stream (and play) such streams over UDP from a file, so it takes required info from file container format. Such solution could take external stream description in SDP format and your actual RTP packets
[EDIT] btw, it's weird that you receive just RTP stream w/o any description of its format, usually its description is provided somehow by RTSP, MPEG-TS or something else
You can forward RTP packets over a UDP socket.
I want to verify the connection status before performing read/write operations.
Is there a way to make an isConnect() method?
I saw this, but it seems "ugly".
I have tested is_open() function as well, but it doesn't have the expected behavior.
TCP is meant to be robust in the face of a harsh network; even though TCP provides what looks like a persistent end-to-end connection, it's all just a lie, each packet is really just a unique, unreliable datagram.
The connections are really just virtual conduits created with a little state tracked at each end of the connection (Source and destination ports and addresses, and local socket). The network stack uses this state to know which process to give each incoming packet to and what state to put in the header of each outgoing packet.
Because of the underlying — inherently connectionless and unreliable — nature of the network, the stack will only report a severed connection when the remote end sends a FIN packet to close the connection, or if it doesn't receive an ACK response to a sent packet (after a timeout and a couple retries).
Because of the asynchronous nature of asio, the easiest way to be notified of a graceful disconnection is to have an outstanding async_read which will return error::eof immediately when the connection is closed. But this alone still leaves the possibility of other issues like half-open connections and network issues going undetected.
The most effectively way to work around unexpected connection interruption is to use some sort of keep-alive or ping. This occasional attempt to transfer data over the connection will allow expedient detection of an unintentionally severed connection.
The TCP protocol actually has a built-in keep-alive mechanism which can be configured in asio using asio::tcp::socket::keep_alive. The nice thing about TCP keep-alive is that it's transparent to the user-mode application, and only the peers interested in keep-alive need configure it. The downside is that you need OS level access/knowledge to configure the timeout parameters, they're unfortunately not exposed via a simple socket option and usually have default timeout values that are quite large (7200 seconds on Linux).
Probably the most common method of keep-alive is to implement it at the application layer, where the application has a special noop or ping message and does nothing but respond when tickled. This method gives you the most flexibility in implementing a keep-alive strategy.
TCP promises to watch for dropped packets -- retrying as appropriate -- to give you a reliable connection, for some definition of reliable. Of course TCP can't handle cases where the server crashes, or your Ethernet cable falls out or something similar occurs. Additionally, knowing that your TCP connection is up doesn't necessarily mean that a protocol that will go over the TCP connection is ready (eg., your HTTP webserver or your FTP server may be in some broken state).
If you know the protocol being sent over TCP then there is probably a way in that protocol to tell you if things are in good shape (for HTTP it would be a HEAD request)
If you are sure that the remote socket has not sent anything (e.g. because you haven't sent a request to it yet), then you can set your local socket to a non blocking mode and try to read one or more bytes from it.
Given that the server hasn't sent anything, you'll either get a asio::error::would_block or some other error. If former, your local socket has not yet detected a disconnection. If latter, your socket has been closed.
Here is an example code:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
using namespace std;
using namespace boost;
using tcp = asio::ip::tcp;
template<class Duration>
void async_sleep(asio::io_service& ios, Duration d, asio::yield_context yield)
{
auto timer = asio::steady_timer(ios);
timer.expires_from_now(d);
timer.async_wait(yield);
}
int main()
{
asio::io_service ios;
tcp::acceptor acceptor(ios, tcp::endpoint(tcp::v4(), 0));
boost::asio::spawn(ios, [&](boost::asio::yield_context yield) {
tcp::socket s(ios);
acceptor.async_accept(s, yield);
// Keep the socket from going out of scope for 5 seconds.
async_sleep(ios, chrono::seconds(5), yield);
});
boost::asio::spawn(ios, [&](boost::asio::yield_context yield) {
tcp::socket s(ios);
s.async_connect(acceptor.local_endpoint(), yield);
// This is essential to make the `read_some` function not block.
s.non_blocking(true);
while (true) {
system::error_code ec;
char c;
// Unfortunately, this only works when the buffer has non
// zero size (tested on Ubuntu 16.04).
s.read_some(asio::mutable_buffer(&c, 1), ec);
if (ec && ec != asio::error::would_block) break;
cerr << "Socket is still connected" << endl;
async_sleep(ios, chrono::seconds(1), yield);
}
cerr << "Socket is closed" << endl;
});
ios.run();
}
And the output:
Socket is still connected
Socket is still connected
Socket is still connected
Socket is still connected
Socket is still connected
Socket is closed
Tested on:
Ubuntu: 16.04
Kernel: 4.15.0-36-generic
Boost: 1.67
Though, I don't know whether or not this behavior depends on any of those versions.
you can send a dummy byte on a socket and see if it will return an error.