Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for RTP input in srt-live-transmit #2848

Merged
merged 8 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/srt-live-transmit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,15 @@ int main(int argc, char** argv)
return 1;
}
break;
case UriParser::RTP:
davemevans marked this conversation as resolved.
Show resolved Hide resolved
if (srt_epoll_add_ssock(pollid,
src->GetSysSocket(), &events))
{
cerr << "Failed to add RTP source to poll, "
<< src->GetSysSocket() << endl;
return 1;
}
break;
case UriParser::FILE:
if (srt_epoll_add_ssock(pollid,
src->GetSysSocket(), &events))
Expand Down
72 changes: 72 additions & 0 deletions apps/transmitmedia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ class UdpCommon

class UdpSource: public Source, public UdpCommon
{
protected:
bool eof = true;
public:

Expand Down Expand Up @@ -1082,6 +1083,68 @@ template <> struct Udp<Target> { typedef UdpTarget type; };
template <class Iface>
Iface* CreateUdp(const string& host, int port, const map<string,string>& par) { return new typename Udp<Iface>::type (host, port, par); }

class RtpSource: public UdpSource
{
// for now, make no effort to parse the header, just assume it is always
// fixed length and either a user-configurable value, or twelve bytes.
const int DEFAULT_RTP_HEADER_SIZE = 12;
const int rtp_header_size = DEFAULT_RTP_HEADER_SIZE;

const int bytes_to_skip = 0;
public:
RtpSource(string host, int port, const map<string,string>& attr) :
UdpSource { host, port, attr },
rtp_header_size {
attr.count("rtpheadersize") ? stoi(attr.at("rtpheadersize"), 0, 0) : DEFAULT_RTP_HEADER_SIZE
},
bytes_to_skip {
(attr.count("droprtpheader") && true_names.count(attr.at("droprtpheader"))) ? rtp_header_size : 0
} {}

int Read(size_t chunk, MediaPacket& pkt, ostream & ignored SRT_ATR_UNUSED = cout) override
{
const int length = UdpSource::Read(chunk, pkt);

if (length < 1 || !bytes_to_skip)
{
// something went wrong, or we're passing headers through
// just return the length read via the base method
return length;
}

// we got some data and we're supposed to skip some of it
// check there's enough bytes for our intended skip
if (length < bytes_to_skip)
{
// something went wrong here
cerr << "RTP packet too short (" << length
<< " bytes) to remove headers (needed "
<< bytes_to_skip << ")" << endl;
throw std::runtime_error("Unexpected RTP packet length");
}

pkt.payload.erase(
pkt.payload.begin(),
pkt.payload.begin() + bytes_to_skip
);

return length - bytes_to_skip;
}
};

class RtpTarget : public UdpTarget {
public:
RtpTarget(string host, int port, const map<string,string>& attr ) :
UdpTarget { host, port, attr } {}
};

template <class Iface> struct Rtp;
template <> struct Rtp<Source> { typedef RtpSource type; };
template <> struct Rtp<Target> { typedef RtpTarget type; };

template <class Iface>
Iface* CreateRtp(const string& host, int port, const map<string,string>& par) { return new typename Rtp<Iface>::type (host, port, par); }

template<class Base>
inline bool IsOutput() { return false; }

Expand Down Expand Up @@ -1141,6 +1204,15 @@ extern unique_ptr<Base> CreateMedium(const string& uri)
ptr.reset( CreateUdp<Base>(u.host(), iport, u.parameters()) );
break;

case UriParser::RTP:
iport = atoi(u.port().c_str());
if ( iport < 1024 )
{
cerr << "Port value invalid: " << iport << " - must be >=1024\n";
throw invalid_argument("Invalid port number");
}
ptr.reset( CreateRtp<Base>(u.host(), iport, u.parameters()) );
break;
}

if (ptr.get())
Expand Down
16 changes: 15 additions & 1 deletion docs/apps/srt-live-transmit.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ srt-live-transmit <input-uri> <output-uri> [options]
The following medium types are handled by `srt-live-transmit`:

- SRT - use SRT for reading or writing, in listener, caller or rendezvous mode, with possibly additional parameters
- UDP - read or write the given UDP address (also multicast)
- UDP/RTP - read or write the given UDP address (also multicast)
- Local file - read or store the stream into the file
- Process's pipeline - use the process's `stdin` and `stdout` standard streams

Expand Down Expand Up @@ -86,6 +86,7 @@ The applications supports the following schemes:

- `file` - for file or standard input and output
- `udp` - UDP output (unicast and multicast)
- `rtp` - RTP over UDP input or output (unicast and multicast)
- `srt` - SRT connection

Note that this application doesn't support file as a medium, but this
Expand Down Expand Up @@ -183,6 +184,19 @@ instead of `IP_ADD_MEMBERSHIP` and the value is set to `imr_sourceaddr` field.
Explanations for the symbols and terms used above can be found in POSIX
manual pages, like `ip(7)` and on Microsoft docs pages under `IPPROTO_IP`.

### Medium: RTP

RTP over UDP is supported for both input and output.
davemevans marked this conversation as resolved.
Show resolved Hide resolved

As an output, and as an input with no RTP-specific URI parameters, RTP medium functions identically to UDP medium as described above.

As an input, additional RTP-specific options are available through URI parameters. These options enable dropping of bytes from the head of the input buffer allowing, for example, the RTP header to be dropped for use cases where the receiving end cannot accept RTP.

- **droprtpheader**: (`bool` as defined in SRT section) - when true, drop the first 12 bytes of each received packet
- **rtpheadersize**: sets the number of bytes to drop from the beginning of each received packet. Ignored if **droprtpheader** is not truthy.
davemevans marked this conversation as resolved.
Show resolved Hide resolved

> NOTE: No effort is made in the initial implementation to attempt to parse the RTP headers in any way eg for reordering, extracting timing, detecting length.

### Medium: SRT

Most important about SRT is that it can be either input or output and in
Expand Down