2014-11-09 11:51:52 +01:00
/*
* rtsp . c : SAT > IP plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author .
*
*/
2014-12-07 19:17:30 +01:00
# define __STDC_FORMAT_MACROS // Required for format specifiers
# include <inttypes.h>
2014-12-03 18:57:23 +01:00
# include "config.h"
2014-11-09 11:51:52 +01:00
# include "common.h"
2014-12-05 22:14:40 +01:00
# include "log.h"
2014-11-09 11:51:52 +01:00
# include "rtsp.h"
cSatipRtsp : : cSatipRtsp ( cSatipTunerIf & tunerP )
2014-11-16 16:02:30 +01:00
: tunerM ( tunerP ) ,
2015-03-22 17:51:39 +01:00
headerBufferM ( ) ,
dataBufferM ( ) ,
2014-12-16 21:35:49 +01:00
handleM ( NULL ) ,
2015-03-18 21:48:52 +01:00
headerListM ( NULL ) ,
2015-03-22 17:51:39 +01:00
errorNoMoreM ( " " ) ,
errorOutOfRangeM ( " " ) ,
2016-12-15 07:49:47 +01:00
errorCheckSyntaxM ( " " ) ,
modeM ( cSatipConfig : : eTransportModeUnicast ) ,
interleavedRtpIdM ( 0 ) ,
interleavedRtcpIdM ( 1 )
2014-11-09 11:51:52 +01:00
{
2014-12-06 16:02:45 +01:00
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
2014-12-16 21:35:49 +01:00
Create ( ) ;
2014-11-09 11:51:52 +01:00
}
cSatipRtsp : : ~ cSatipRtsp ( )
{
2014-12-06 16:02:45 +01:00
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
2014-12-16 21:35:49 +01:00
Destroy ( ) ;
2014-11-09 11:51:52 +01:00
}
2015-03-22 17:51:39 +01:00
size_t cSatipRtsp : : HeaderCallback ( char * ptrP , size_t sizeP , size_t nmembP , void * dataP )
2014-11-09 11:51:52 +01:00
{
cSatipRtsp * obj = reinterpret_cast < cSatipRtsp * > ( dataP ) ;
size_t len = sizeP * nmembP ;
2014-12-14 16:45:55 +01:00
debug16 ( " %s len=%zu " , __PRETTY_FUNCTION__ , len ) ;
2014-11-09 11:51:52 +01:00
2014-11-16 16:02:30 +01:00
if ( obj & & ( len > 0 ) )
2015-03-22 17:51:39 +01:00
obj - > headerBufferM . Add ( ptrP , len ) ;
2014-11-09 11:51:52 +01:00
return len ;
}
2015-03-22 17:51:39 +01:00
size_t cSatipRtsp : : DataCallback ( char * ptrP , size_t sizeP , size_t nmembP , void * dataP )
2015-03-18 21:48:52 +01:00
{
cSatipRtsp * obj = reinterpret_cast < cSatipRtsp * > ( dataP ) ;
size_t len = sizeP * nmembP ;
debug16 ( " %s len=%zu " , __PRETTY_FUNCTION__ , len ) ;
2015-03-22 17:51:39 +01:00
if ( obj )
obj - > dataBufferM . Add ( ptrP , len ) ;
2015-03-18 21:48:52 +01:00
return len ;
}
2016-12-15 07:49:47 +01:00
size_t cSatipRtsp : : InterleaveCallback ( char * ptrP , size_t sizeP , size_t nmembP , void * dataP )
{
cSatipRtsp * obj = reinterpret_cast < cSatipRtsp * > ( dataP ) ;
size_t len = sizeP * nmembP ;
debug16 ( " %s len=%zu " , __PRETTY_FUNCTION__ , len ) ;
if ( obj & & ptrP & & len > 0 ) {
char tag = ptrP [ 0 ] & 0xFF ;
if ( tag = = ' $ ' ) {
int count = ( ( ptrP [ 2 ] & 0xFF ) < < 8 ) | ( ptrP [ 3 ] & 0xFF ) ;
if ( count > 0 ) {
unsigned int channel = ptrP [ 1 ] & 0xFF ;
u_char * data = ( u_char * ) & ptrP [ 4 ] ;
if ( channel = = obj - > interleavedRtpIdM )
obj - > tunerM . ProcessRtpData ( data , count ) ;
else if ( channel = = obj - > interleavedRtcpIdM )
obj - > tunerM . ProcessRtcpData ( data , count ) ;
}
}
}
return len ;
}
2014-11-09 11:51:52 +01:00
int cSatipRtsp : : DebugCallback ( CURL * handleP , curl_infotype typeP , char * dataP , size_t sizeP , void * userPtrP )
{
cSatipRtsp * obj = reinterpret_cast < cSatipRtsp * > ( userPtrP ) ;
2014-11-16 16:02:30 +01:00
if ( obj ) {
2014-11-09 11:51:52 +01:00
switch ( typeP ) {
case CURLINFO_TEXT :
2014-12-06 16:02:45 +01:00
debug2 ( " %s [device %d] RTSP INFO %.*s " , __PRETTY_FUNCTION__ , obj - > tunerM . GetId ( ) , ( int ) sizeP , dataP ) ;
2014-11-09 11:51:52 +01:00
break ;
case CURLINFO_HEADER_IN :
2014-12-06 16:02:45 +01:00
debug2 ( " %s [device %d] RTSP HEAD <<< %.*s " , __PRETTY_FUNCTION__ , obj - > tunerM . GetId ( ) , ( int ) sizeP , dataP ) ;
2014-11-09 11:51:52 +01:00
break ;
case CURLINFO_HEADER_OUT :
2014-12-06 16:02:45 +01:00
debug2 ( " %s [device %d] RTSP HEAD >>> \n %.*s " , __PRETTY_FUNCTION__ , obj - > tunerM . GetId ( ) , ( int ) sizeP , dataP ) ;
2014-11-09 11:51:52 +01:00
break ;
case CURLINFO_DATA_IN :
2014-12-06 16:02:45 +01:00
debug2 ( " %s [device %d] RTSP DATA <<< %.*s " , __PRETTY_FUNCTION__ , obj - > tunerM . GetId ( ) , ( int ) sizeP , dataP ) ;
2014-11-09 11:51:52 +01:00
break ;
case CURLINFO_DATA_OUT :
2014-12-06 16:02:45 +01:00
debug2 ( " %s [device %d] RTSP DATA >>> \n %.*s " , __PRETTY_FUNCTION__ , obj - > tunerM . GetId ( ) , ( int ) sizeP , dataP ) ;
2014-11-09 11:51:52 +01:00
break ;
default :
break ;
}
}
return 0 ;
}
2016-12-15 07:49:47 +01:00
cString cSatipRtsp : : GetActiveMode ( void )
{
switch ( modeM ) {
case cSatipConfig : : eTransportModeUnicast :
return " Unicast " ;
case cSatipConfig : : eTransportModeMulticast :
return " Multicast " ;
case cSatipConfig : : eTransportModeRtpOverTcp :
return " RTP-over-TCP " ;
default :
break ;
}
return " " ;
}
2014-11-09 11:51:52 +01:00
cString cSatipRtsp : : RtspUnescapeString ( const char * strP )
{
2014-12-07 16:27:53 +01:00
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , strP , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
if ( handleM ) {
char * p = curl_easy_unescape ( handleM , strP , 0 , NULL ) ;
cString s = p ;
curl_free ( p ) ;
return s ;
}
return cString ( strP ) ;
}
2014-12-16 21:35:49 +01:00
void cSatipRtsp : : Create ( void )
{
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
if ( ! handleM )
handleM = curl_easy_init ( ) ;
if ( handleM ) {
CURLcode res = CURLE_OK ;
// Verbose output
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_VERBOSE , 1L ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_DEBUGFUNCTION , cSatipRtsp : : DebugCallback ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_DEBUGDATA , this ) ;
// No progress meter and no signaling
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_NOPROGRESS , 1L ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_NOSIGNAL , 1L ) ;
// Set timeouts
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_TIMEOUT_MS , ( long ) eConnectTimeoutMs ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_CONNECTTIMEOUT_MS , ( long ) eConnectTimeoutMs ) ;
// Set user-agent
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_USERAGENT , * cString : : sprintf ( " vdr-%s/%s (device %d) " , PLUGIN_NAME_I18N , VERSION , tunerM . GetId ( ) ) ) ;
}
}
void cSatipRtsp : : Destroy ( void )
{
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
if ( handleM ) {
// Cleanup curl stuff
if ( headerListM ) {
curl_slist_free_all ( headerListM ) ;
headerListM = NULL ;
}
curl_easy_cleanup ( handleM ) ;
handleM = NULL ;
}
}
void cSatipRtsp : : Reset ( void )
{
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
Destroy ( ) ;
Create ( ) ;
}
2017-07-19 16:47:22 +02:00
bool cSatipRtsp : : SetInterface ( const char * bindAddrP )
{
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , bindAddrP , tunerM . GetId ( ) ) ;
bool result = true ;
CURLcode res = CURLE_OK ;
if ( handleM & & ! isempty ( bindAddrP ) ) {
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERFACE , * cString : : sprintf ( " host!%s " , bindAddrP ) ) ;
}
else {
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERFACE , NULL ) ;
}
return result ;
}
2018-10-21 19:21:33 +02:00
bool cSatipRtsp : : Receive ( const char * uriP )
{
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , uriP , tunerM . GetId ( ) ) ;
bool result = false ;
if ( handleM & & ! isempty ( uriP ) & & modeM = = cSatipConfig : : eTransportModeRtpOverTcp ) {
long rc = 0 ;
cTimeMs processing ( 0 ) ;
CURLcode res = CURLE_OK ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_URL , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_STREAM_URI , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_REQUEST , ( long ) CURL_RTSPREQ_OPTIONS ) ; // FIXME: this really should be CURL_RTSPREQ_RECEIVE, but getting timeout errors
SATIP_CURL_EASY_PERFORM ( handleM ) ;
result = ValidateLatestResponse ( & rc ) ;
debug5 ( " %s (%s) Response %ld in % " PRIu64 " ms [device %d] " , __PRETTY_FUNCTION__ , uriP , rc , processing . Elapsed ( ) , tunerM . GetId ( ) ) ;
}
return result ;
}
2014-11-09 11:51:52 +01:00
bool cSatipRtsp : : Options ( const char * uriP )
{
2014-12-07 16:27:53 +01:00
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , uriP , tunerM . GetId ( ) ) ;
2014-12-07 19:17:30 +01:00
bool result = false ;
2014-11-09 11:51:52 +01:00
if ( handleM & & ! isempty ( uriP ) ) {
2014-12-07 19:17:30 +01:00
long rc = 0 ;
cTimeMs processing ( 0 ) ;
2014-11-09 11:51:52 +01:00
CURLcode res = CURLE_OK ;
2014-12-20 17:46:00 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_URL , uriP ) ;
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_STREAM_URI , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_REQUEST , ( long ) CURL_RTSPREQ_OPTIONS ) ;
SATIP_CURL_EASY_PERFORM ( handleM ) ;
2014-12-07 19:17:30 +01:00
result = ValidateLatestResponse ( & rc ) ;
debug5 ( " %s (%s) Response %ld in % " PRIu64 " ms [device %d] " , __PRETTY_FUNCTION__ , uriP , rc , processing . Elapsed ( ) , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
}
2014-12-07 19:17:30 +01:00
return result ;
2014-11-09 11:51:52 +01:00
}
2016-06-20 17:14:38 +02:00
bool cSatipRtsp : : Setup ( const char * uriP , int rtpPortP , int rtcpPortP , bool useTcpP )
2014-11-09 11:51:52 +01:00
{
2016-06-20 17:14:38 +02:00
debug1 ( " %s (%s, %d, %d, %d) [device %d] " , __PRETTY_FUNCTION__ , uriP , rtpPortP , rtcpPortP , useTcpP , tunerM . GetId ( ) ) ;
2014-12-07 19:17:30 +01:00
bool result = false ;
2014-11-09 11:51:52 +01:00
if ( handleM & & ! isempty ( uriP ) ) {
2014-12-21 15:31:38 +01:00
cString transport ;
2014-12-07 19:17:30 +01:00
long rc = 0 ;
cTimeMs processing ( 0 ) ;
2014-11-09 11:51:52 +01:00
CURLcode res = CURLE_OK ;
2014-12-21 15:31:38 +01:00
2016-12-11 00:18:12 +01:00
switch ( SatipConfig . GetTransportMode ( ) ) {
case cSatipConfig : : eTransportModeMulticast :
// RTP/AVP;multicast;destination=<multicast group address>;port=<RTP port>-<RTCP port>;ttl=<ttl>[;source=<multicast source address>]
transport = cString : : sprintf ( " RTP/AVP;multicast " ) ;
2014-12-21 15:31:38 +01:00
break ;
default :
// RTP/AVP;unicast;client_port=<client RTP port>-<client RTCP port>
2016-06-20 17:14:38 +02:00
// RTP/AVP/TCP;unicast;client_port=<client RTP port>-<client RTCP port>
2016-12-15 07:49:47 +01:00
if ( useTcpP )
transport = cString : : sprintf ( " RTP/AVP/TCP;unicast;interleaved=%u-%u " , interleavedRtpIdM , interleavedRtcpIdM ) ;
else
transport = cString : : sprintf ( " RTP/AVP;unicast;client_port=%d-%d " , rtpPortP , rtcpPortP ) ;
2014-12-21 15:31:38 +01:00
break ;
}
2014-11-09 11:51:52 +01:00
// Setup media stream
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_STREAM_URI , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_TRANSPORT , * transport ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_REQUEST , ( long ) CURL_RTSPREQ_SETUP ) ;
// Set header callback for catching the session and timeout
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_HEADERFUNCTION , cSatipRtsp : : HeaderCallback ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEHEADER , this ) ;
2015-03-22 17:51:39 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , cSatipRtsp : : DataCallback ) ;
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , this ) ;
2016-12-15 07:49:47 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEDATA , NULL ) ;
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_PERFORM ( handleM ) ;
// Session id is now known - disable header parsing
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_HEADERFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEHEADER , NULL ) ;
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , NULL ) ;
2015-03-22 17:51:39 +01:00
if ( headerBufferM . Size ( ) > 0 ) {
ParseHeader ( ) ;
headerBufferM . Reset ( ) ;
}
if ( dataBufferM . Size ( ) > 0 ) {
ParseData ( ) ;
dataBufferM . Reset ( ) ;
}
2014-11-09 11:51:52 +01:00
2014-12-07 19:17:30 +01:00
result = ValidateLatestResponse ( & rc ) ;
debug5 ( " %s (%s, %d, %d) Response %ld in % " PRIu64 " ms [device %d] " , __PRETTY_FUNCTION__ , uriP , rtpPortP , rtcpPortP , rc , processing . Elapsed ( ) , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
}
2014-12-07 19:17:30 +01:00
return result ;
2014-11-09 11:51:52 +01:00
}
bool cSatipRtsp : : SetSession ( const char * sessionP )
{
2014-12-07 16:27:53 +01:00
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , sessionP , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
if ( handleM ) {
CURLcode res = CURLE_OK ;
2014-12-06 16:02:45 +01:00
debug1 ( " %s: session id quirk enabled [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_SESSION_ID , sessionP ) ;
}
return true ;
}
bool cSatipRtsp : : Describe ( const char * uriP )
{
2014-12-07 16:27:53 +01:00
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , uriP , tunerM . GetId ( ) ) ;
2014-12-07 19:17:30 +01:00
bool result = false ;
2014-11-09 11:51:52 +01:00
if ( handleM & & ! isempty ( uriP ) ) {
2014-12-07 19:17:30 +01:00
long rc = 0 ;
cTimeMs processing ( 0 ) ;
2014-11-09 11:51:52 +01:00
CURLcode res = CURLE_OK ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_STREAM_URI , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_REQUEST , ( long ) CURL_RTSPREQ_DESCRIBE ) ;
2015-03-22 17:51:39 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , cSatipRtsp : : DataCallback ) ;
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , this ) ;
SATIP_CURL_EASY_PERFORM ( handleM ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , NULL ) ;
2015-03-22 17:51:39 +01:00
if ( dataBufferM . Size ( ) > 0 ) {
tunerM . ProcessApplicationData ( ( u_char * ) dataBufferM . Data ( ) , dataBufferM . Size ( ) ) ;
dataBufferM . Reset ( ) ;
}
2014-11-09 11:51:52 +01:00
2014-12-07 19:17:30 +01:00
result = ValidateLatestResponse ( & rc ) ;
debug5 ( " %s (%s) Response %ld in % " PRIu64 " ms [device %d] " , __PRETTY_FUNCTION__ , uriP , rc , processing . Elapsed ( ) , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
}
2014-12-07 19:17:30 +01:00
return result ;
2014-11-09 11:51:52 +01:00
}
bool cSatipRtsp : : Play ( const char * uriP )
{
2014-12-07 16:27:53 +01:00
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , uriP , tunerM . GetId ( ) ) ;
2014-12-07 19:17:30 +01:00
bool result = false ;
2014-11-09 11:51:52 +01:00
if ( handleM & & ! isempty ( uriP ) ) {
2014-12-07 19:17:30 +01:00
long rc = 0 ;
cTimeMs processing ( 0 ) ;
2014-11-09 11:51:52 +01:00
CURLcode res = CURLE_OK ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_STREAM_URI , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_REQUEST , ( long ) CURL_RTSPREQ_PLAY ) ;
2015-03-22 17:51:39 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , cSatipRtsp : : DataCallback ) ;
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , this ) ;
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_PERFORM ( handleM ) ;
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , NULL ) ;
2015-03-22 17:51:39 +01:00
if ( dataBufferM . Size ( ) > 0 ) {
ParseData ( ) ;
dataBufferM . Reset ( ) ;
}
2014-11-09 11:51:52 +01:00
2014-12-07 19:17:30 +01:00
result = ValidateLatestResponse ( & rc ) ;
debug5 ( " %s (%s) Response %ld in % " PRIu64 " ms [device %d] " , __PRETTY_FUNCTION__ , uriP , rc , processing . Elapsed ( ) , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
}
2014-12-07 19:17:30 +01:00
return result ;
2014-11-09 11:51:52 +01:00
}
bool cSatipRtsp : : Teardown ( const char * uriP )
{
2014-12-07 16:27:53 +01:00
debug1 ( " %s (%s) [device %d] " , __PRETTY_FUNCTION__ , uriP , tunerM . GetId ( ) ) ;
2014-12-07 19:17:30 +01:00
bool result = false ;
2014-11-09 11:51:52 +01:00
if ( handleM & & ! isempty ( uriP ) ) {
2014-12-07 19:17:30 +01:00
long rc = 0 ;
cTimeMs processing ( 0 ) ;
2014-11-09 11:51:52 +01:00
CURLcode res = CURLE_OK ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_STREAM_URI , uriP ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_REQUEST , ( long ) CURL_RTSPREQ_TEARDOWN ) ;
2015-03-22 17:51:39 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , cSatipRtsp : : DataCallback ) ;
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , this ) ;
2016-12-15 07:49:47 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEDATA , NULL ) ;
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_PERFORM ( handleM ) ;
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_WRITEDATA , NULL ) ;
2015-03-22 17:51:39 +01:00
if ( dataBufferM . Size ( ) > 0 ) {
ParseData ( ) ;
dataBufferM . Reset ( ) ;
}
2014-11-09 11:51:52 +01:00
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_CLIENT_CSEQ , 1L ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_RTSP_SESSION_ID , NULL ) ;
2014-12-07 19:17:30 +01:00
result = ValidateLatestResponse ( & rc ) ;
debug5 ( " %s (%s) Response %ld in % " PRIu64 " ms [device %d] " , __PRETTY_FUNCTION__ , uriP , rc , processing . Elapsed ( ) , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
}
2014-12-07 19:17:30 +01:00
return result ;
2014-11-09 11:51:52 +01:00
}
2015-03-22 17:51:39 +01:00
void cSatipRtsp : : ParseHeader ( void )
2015-03-18 21:48:52 +01:00
{
2015-03-22 17:51:39 +01:00
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
char * s , * p = headerBufferM . Data ( ) ;
char * r = strtok_r ( p , " \r \n " , & s ) ;
2015-03-18 21:48:52 +01:00
2015-03-22 17:51:39 +01:00
while ( r ) {
debug16 ( " %s (%zu): %s " , __PRETTY_FUNCTION__ , headerBufferM . Size ( ) , r ) ;
r = skipspace ( r ) ;
if ( strstr ( r , " com.ses.streamID " ) ) {
int streamid = - 1 ;
if ( sscanf ( r , " com.ses.streamID:%11d " , & streamid ) = = 1 )
tunerM . SetStreamId ( streamid ) ;
}
else if ( strstr ( r , " Session: " ) ) {
int timeout = - 1 ;
char * session = NULL ;
if ( sscanf ( r , " Session:%m[^;];timeout=%11d " , & session , & timeout ) = = 2 )
tunerM . SetSessionTimeout ( skipspace ( session ) , timeout * 1000 ) ;
else if ( sscanf ( r , " Session:%m[^;] " , & session ) = = 1 )
tunerM . SetSessionTimeout ( skipspace ( session ) , - 1 ) ;
FREE_POINTER ( session ) ;
}
2016-12-11 00:18:12 +01:00
else if ( strstr ( r , " Transport: " ) ) {
2016-12-15 07:49:47 +01:00
CURLcode res = CURLE_OK ;
2016-12-11 00:18:12 +01:00
int rtp = - 1 , rtcp = - 1 , ttl = - 1 ;
char * tmp = NULL , * destination = NULL , * source = NULL ;
2016-12-15 07:49:47 +01:00
interleavedRtpIdM = 0 ;
interleavedRtcpIdM = 1 ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEFUNCTION , NULL ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEDATA , NULL ) ;
if ( sscanf ( r , " Transport:%m[^;];unicast;client_port=%11d-%11d " , & tmp , & rtp , & rtcp ) = = 3 ) {
modeM = cSatipConfig : : eTransportModeUnicast ;
2016-12-11 00:18:12 +01:00
tunerM . SetupTransport ( rtp , rtcp , NULL , NULL ) ;
2016-12-15 07:49:47 +01:00
}
2016-12-11 00:18:12 +01:00
else if ( sscanf ( r , " Transport:%m[^;];multicast;destination=%m[^;];port=%11d-%11d;ttl=%11d;source=%m[^;] " , & tmp , & destination , & rtp , & rtcp , & ttl , & source ) = = 6 | |
2016-12-15 07:49:47 +01:00
sscanf ( r , " Transport:%m[^;];multicast;destination=%m[^;];port=%11d-%11d;ttl=%11d " , & tmp , & destination , & rtp , & rtcp , & ttl ) = = 5 ) {
modeM = cSatipConfig : : eTransportModeMulticast ;
2016-12-11 00:18:12 +01:00
tunerM . SetupTransport ( rtp , rtcp , destination , source ) ;
2016-12-15 07:49:47 +01:00
}
else if ( sscanf ( r , " Transport:%m[^;];interleaved=%11d-%11d " , & tmp , & rtp , & rtcp ) = = 3 ) {
interleavedRtpIdM = rtp ;
interleavedRtcpIdM = rtcp ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEFUNCTION , cSatipRtsp : : InterleaveCallback ) ;
SATIP_CURL_EASY_SETOPT ( handleM , CURLOPT_INTERLEAVEDATA , this ) ;
modeM = cSatipConfig : : eTransportModeRtpOverTcp ;
tunerM . SetupTransport ( - 1 , - 1 , NULL , NULL ) ;
}
2016-12-11 00:18:12 +01:00
FREE_POINTER ( tmp ) ;
FREE_POINTER ( destination ) ;
FREE_POINTER ( source ) ;
}
2015-03-22 17:51:39 +01:00
r = strtok_r ( NULL , " \r \n " , & s ) ;
}
2015-03-18 21:48:52 +01:00
}
2015-03-22 17:51:39 +01:00
void cSatipRtsp : : ParseData ( void )
2015-03-18 21:48:52 +01:00
{
2015-03-22 17:51:39 +01:00
debug1 ( " %s [device %d] " , __PRETTY_FUNCTION__ , tunerM . GetId ( ) ) ;
char * s , * p = dataBufferM . Data ( ) ;
char * r = strtok_r ( p , " \r \n " , & s ) ;
while ( r ) {
debug16 ( " %s (%zu): %s " , __PRETTY_FUNCTION__ , dataBufferM . Size ( ) , r ) ;
r = skipspace ( r ) ;
if ( strstr ( r , " No-More: " ) ) {
char * tmp = NULL ;
if ( sscanf ( r , " No-More:%m[^;] " , & tmp ) = = 1 ) {
errorNoMoreM = skipspace ( tmp ) ;
debug3 ( " %s No-More: %s [device %d] " , __PRETTY_FUNCTION__ , * errorNoMoreM , tunerM . GetId ( ) ) ;
}
FREE_POINTER ( tmp ) ;
}
else if ( strstr ( r , " Out-of-Range: " ) ) {
char * tmp = NULL ;
if ( sscanf ( r , " Out-of-Range:%m[^;] " , & tmp ) = = 1 ) {
errorOutOfRangeM = skipspace ( tmp ) ;
debug3 ( " %s Out-of-Range: %s [device %d] " , __PRETTY_FUNCTION__ , * errorOutOfRangeM , tunerM . GetId ( ) ) ;
}
FREE_POINTER ( tmp ) ;
}
else if ( strstr ( r , " Check-Syntax: " ) ) {
char * tmp = NULL ;
if ( sscanf ( r , " Check-Syntax:%m[^;] " , & tmp ) = = 1 ) {
errorCheckSyntaxM = skipspace ( tmp ) ;
debug3 ( " %s Check-Syntax: %s [device %d] " , __PRETTY_FUNCTION__ , * errorCheckSyntaxM , tunerM . GetId ( ) ) ;
}
FREE_POINTER ( tmp ) ;
}
r = strtok_r ( NULL , " \r \n " , & s ) ;
}
2015-03-18 21:48:52 +01:00
}
2014-12-07 19:17:30 +01:00
bool cSatipRtsp : : ValidateLatestResponse ( long * rcP )
2014-11-09 11:51:52 +01:00
{
2014-11-15 19:15:00 +01:00
bool result = false ;
2014-12-07 19:17:30 +01:00
2014-11-09 11:51:52 +01:00
if ( handleM ) {
2015-03-18 21:48:52 +01:00
char * url = NULL ;
2014-11-09 11:51:52 +01:00
long rc = 0 ;
CURLcode res = CURLE_OK ;
SATIP_CURL_EASY_GETINFO ( handleM , CURLINFO_RESPONSE_CODE , & rc ) ;
2015-03-18 21:48:52 +01:00
switch ( rc ) {
case 200 :
result = true ;
break ;
case 400 :
// SETUP PLAY TEARDOWN
// The message body of the response may contain the "Check-Syntax:" parameter followed
// by the malformed syntax
2015-03-22 17:51:39 +01:00
if ( ! isempty ( * errorCheckSyntaxM ) ) {
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_GETINFO ( handleM , CURLINFO_EFFECTIVE_URL , & url ) ;
2015-03-22 17:51:39 +01:00
error ( " Check syntax: %s (error code %ld: %s) [device %d] " , * errorCheckSyntaxM , rc , url , tunerM . GetId ( ) ) ;
2015-03-18 21:48:52 +01:00
break ;
}
case 403 :
// SETUP PLAY TEARDOWN
// The message body of the response may contain the "Out-of-Range:" parameter followed
// by a space-separated list of the attribute names that are not understood:
2016-09-09 18:59:12 +02:00
// "src" "fe" "freq" "pol" "msys" "mtype" "plts" "ro" "sr" "fec" "pids" "addpids" "delpids" "mcast"
2015-03-22 17:51:39 +01:00
if ( ! isempty ( * errorOutOfRangeM ) ) {
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_GETINFO ( handleM , CURLINFO_EFFECTIVE_URL , & url ) ;
2015-03-22 17:51:39 +01:00
error ( " Out of range: %s (error code %ld: %s) [device %d] " , * errorOutOfRangeM , rc , url , tunerM . GetId ( ) ) ;
2016-09-09 18:59:12 +02:00
// Reseting the connection wouldn't help anything due to invalid channel configuration, so let it be successful
result = true ;
2015-03-18 21:48:52 +01:00
break ;
}
case 503 :
// SETUP PLAY
// The message body of the response may contain the "No-More:" parameter followed
// by a space-separated list of the missing ressources: “sessions” "frontends" "pids
2015-03-22 17:51:39 +01:00
if ( ! isempty ( * errorNoMoreM ) ) {
2015-03-18 21:48:52 +01:00
SATIP_CURL_EASY_GETINFO ( handleM , CURLINFO_EFFECTIVE_URL , & url ) ;
2015-03-22 17:51:39 +01:00
error ( " No more: %s (error code %ld: %s) [device %d] " , * errorNoMoreM , rc , url , tunerM . GetId ( ) ) ;
2015-03-18 21:48:52 +01:00
break ;
}
default :
SATIP_CURL_EASY_GETINFO ( handleM , CURLINFO_EFFECTIVE_URL , & url ) ;
error ( " Detected invalid status code %ld: %s [device %d] " , rc , url , tunerM . GetId ( ) ) ;
break ;
}
2014-12-07 19:17:30 +01:00
if ( rcP )
* rcP = rc ;
2014-11-09 11:51:52 +01:00
}
2015-03-22 17:51:39 +01:00
errorNoMoreM = " " ;
errorOutOfRangeM = " " ;
errorCheckSyntaxM = " " ;
2014-12-06 16:02:45 +01:00
debug1 ( " %s result=%s [device %d] " , __PRETTY_FUNCTION__ , result ? " ok " : " failed " , tunerM . GetId ( ) ) ;
2014-11-09 11:51:52 +01:00
2014-11-15 19:15:00 +01:00
return result ;
2014-11-09 11:51:52 +01:00
}