예제 #1
0
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
  mId( id ),
  mSsrc( ssrc ),
  mLocalHost( localHost ),
  mRemoteHost( remoteHost ),
  mRtpClock( rtpClock ),
  mCodecId( codecId ),
  mFrame( 65536 ),
  mFrameCount( 0 ),
  mFrameGood( true ),
  mFrameReady( false ),
  mFrameProcessed( false )
{
  char hostname[256] = "";
  gethostname( hostname, sizeof(hostname) );

  mCname = stringtf( "zm-%d@%s", mId, hostname );
  Debug( 3, "RTP CName = %s", mCname.c_str() );

  init( seq );
  mMaxSeq = seq - 1;
  mProbation = MIN_SEQUENTIAL;

  mLocalPortChans[0] = localPortBase;
  mLocalPortChans[1] = localPortBase+1;

  mRemotePortChans[0] = remotePortBase;
  mRemotePortChans[1] = remotePortBase+1;

  mRtpFactor = mRtpClock;

  mBaseTimeReal = tvNow();
  mBaseTimeNtp = tvZero();
  mBaseTimeRtp = rtpTime;

  mLastSrTimeReal = tvZero();
  mLastSrTimeNtp = tvZero();
  mLastSrTimeRtp = 0;
  
  if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4)
    Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
}
예제 #2
0
RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe) :
    mId( id ),
    mMethod( method ),
    mProtocol( protocol ),
    mHost( host ),
    mPort( port ),
    mPath( path ),
    mRtspDescribe( rtsp_describe ),
    mSessDesc( 0 ),
    mFormatContext( 0 ),
    mSeq( 0 ),
    mSession( 0 ),
    mSsrc( 0 ),
    mDist( UNDEFINED ),
    mRtpTime( 0 ),
    mStop( false )
{
    mUrl = mProtocol+"://"+mHost+":"+mPort;
    if ( !mPath.empty() )
    {
        if ( mPath[0] == '/' )
            mUrl += mPath;
        else
            mUrl += '/'+mPath;
    }

    mSsrc = rand();

    Debug( 2, "RTSP Local SSRC is %x", mSsrc );

    if ( mMethod == RTP_RTSP_HTTP )
        mHttpSession = stringtf( "%d", rand() );

    mNeedAuth = false;
    StringVector parts = split(auth,":");
    if (parts.size() > 1)
        mAuthenticator = new Authenticator(parts[0], parts[1]);
    else
        mAuthenticator = new Authenticator(parts[0], "");
}
예제 #3
0
/**
* @brief 
*
* @return 
*/
int LocalFileDump::run()
{
    std::string filePath;
    FILE *fileDesc = NULL;

    if ( waitForProviders() )
    {
        while( !mStop )
        {
            mQueueMutex.lock();
            if ( !mFrameQueue.empty() )
            {
                for ( FrameQueue::iterator iter = mFrameQueue.begin(); iter != mFrameQueue.end(); iter++ )
                {
                    const FeedFrame *frame = iter->get();
                    Info( "F:%ld", frame->buffer().size() );
                    if ( filePath.empty() )
                    {
                        filePath = stringtf( "%s/%s-%s", mLocation.c_str(), mName.c_str(), frame->provider()->cidentity() );
                        Info( "Path: %s", filePath.c_str() );
                        fileDesc = fopen( filePath.c_str(), "w" );
                        if ( !fileDesc )
                            Fatal( "Failed to open dump file '%s': %s", filePath.c_str(), strerror(errno) );
                    }
                    if ( fwrite( frame->buffer().data(), frame->buffer().size(), 1, fileDesc ) <= 0 )
                        Fatal( "Failed to write to dump file '%s': %s", filePath.c_str(), strerror(errno) );
                    //delete *iter;
                }
                mFrameQueue.clear();
            }
            mQueueMutex.unlock();
            checkProviders();
            usleep( INTERFRAME_TIMEOUT );
        }
        fclose( fileDesc );
    }
    cleanup();
    return( 0 );
}
예제 #4
0
void Mutex::unlock()
{
  if ( pthread_mutex_unlock( &mMutex ) < 0 )
    throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
}
예제 #5
0
void Mutex::lock( double secs )
{
  struct timespec timeout = getTimeout( secs );
  if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
    throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
}
예제 #6
0
Mutex::Mutex()
{
  if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
    throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
}
예제 #7
0
/**
* @brief 
*
* @param request
*
* @return 
*/
bool RtspConnection::handleRequest( const std::string &request )
{
    Debug( 2, "Handling RTSP request: %s (%zd bytes)", request.c_str(), request.size() );

    StringTokenList lines( request, "\r\n" );
    if ( lines.size() <= 0 )
    {
        Error( "Unable to split request '%s' into tokens", request.c_str() );
        return( false );
    }

    StringTokenList parts( lines[0], " " );
    if ( parts.size() != 3 )
    {
        Error( "Unable to split request part '%s' into tokens", lines[0].c_str() );
        return( false );
    }

    std::string requestType = parts[0];
    Debug( 4, "Got request '%s'", requestType.c_str() );
    std::string requestUrl = parts[1];
    Debug( 4, "Got requestUrl '%s'", requestUrl.c_str() );
    std::string requestVer = parts[2];
    Debug( 4, "Got requestVer '%s'", requestVer.c_str() );
    if ( requestVer != "RTSP/1.0" )
    {
        Error( "Unexpected RTSP version '%s'", requestVer.c_str() );
        return( false );
    }

    // Extract headers from request
    Headers requestHeaders;
    for ( int i = 1; i < lines.size(); i++ )
    {
        StringTokenList parts( lines[i], ": " );
        if ( parts.size() != 2 )
        {
            Error( "Unable to split request header '%s' into tokens", lines[i].c_str() );
            return( false );
        }
        Debug( 4, "Got header '%s', value '%s'", parts[0].c_str(), parts[1].c_str() );
        requestHeaders.insert( Headers::value_type( parts[0], parts[1] ) );
    }

    if ( requestHeaders.find("CSeq") == requestHeaders.end() )
    {
        Error( "No CSeq header found" );
        return( false );
    }
    Debug( 4, "Got sequence number %s", requestHeaders["CSeq"].c_str() );

    uint32_t session = 0;
    if ( requestHeaders.find("Session") != requestHeaders.end() )
    {
        Debug( 4, "Got session header, '%s', passing to session", requestHeaders["Session"].c_str() );
        session = strtol( requestHeaders["Session"].c_str(), NULL, 16 );
    }

    Headers responseHeaders;
    responseHeaders.insert( Headers::value_type( "CSeq", requestHeaders["CSeq"] ) );
    if ( requestType == "OPTIONS" )
    {
        responseHeaders.insert( Headers::value_type( "Public", "DESCRIBE, SETUP, PLAY, GET_PARAMETER, TEARDOWN" ) );
        return( sendResponse( responseHeaders ) );
    }
    else if ( requestType == "DESCRIBE" )
    {
        FeedProvider *provider = validateRequestUrl( requestUrl );

        if ( !provider )
        {
            sendResponse( responseHeaders, "", 404, "Not Found" );
            return( false );
        }

        const VideoProvider *videoProvider = dynamic_cast<const VideoProvider *>(provider);

        int codec = AV_CODEC_ID_MPEG4;
        int width = videoProvider->width();
        int height = videoProvider->height();
        FrameRate frameRate = 15;
        //FrameRate frameRate = videoProvider->frameRate();
        int bitRate = 90000;
        int quality = 70;

        std::string sdpFormatString =
            "v=0\r\n"
            "o=- %jd %jd IN IP4 %s\r\n"
            "s=ZoneMinder Stream\r\n"
            "i=Media Streamers\r\n"
            "c=IN IP4 0.0.0.0\r\n"
            "t=0 0\r\n"
            "a=control:*\r\n"
            "a=range:npt=0.000000-\r\n";
        uint64_t now64 = time64();
        char hostname[HOST_NAME_MAX] = "";
        if ( gethostname( hostname, sizeof(hostname) ) < 0 )
            Fatal( "Can't gethostname: %s", strerror(errno) );

        std::string sdpString = stringtf( sdpFormatString, now64, now64, hostname );

        if ( codec == AV_CODEC_ID_H264 )
        {
            if ( provider->cl4ss() == "RawH264Input" )
            {
                std::string encoderKey = H264Relay::getPoolKey( provider->identity(), width, height, frameRate, bitRate, quality );
                if ( !(mEncoder = Encoder::getPooledEncoder( encoderKey )) )
                {
                    H264Relay *h264Relay = NULL;
                    mEncoder = h264Relay = new H264Relay( provider->identity(), width, height, frameRate, bitRate, quality );
                    mEncoder->registerProvider( *provider );
                    Encoder::poolEncoder( mEncoder );
                    h264Relay->start();
                }
                sdpString += mEncoder->sdpString( 1 ); // XXX - Should be variable
                responseHeaders.insert( Headers::value_type( "Content-length", stringtf( "%zd", sdpString.length() ) ) );
            }
            else
            {
                std::string encoderKey = H264Encoder::getPoolKey( provider->identity(), width, height, frameRate, bitRate, quality );
                if ( !(mEncoder = Encoder::getPooledEncoder( encoderKey )) )
                {
                    H264Encoder *h264Encoder = NULL;
                    mEncoder = h264Encoder = new H264Encoder( provider->identity(), width, height, frameRate, bitRate, quality );
                    mEncoder->registerProvider( *provider );
                    Encoder::poolEncoder( mEncoder );
                    h264Encoder->start();
                }
                sdpString += mEncoder->sdpString( 1 ); // XXX - Should be variable
                responseHeaders.insert( Headers::value_type( "Content-length", stringtf( "%zd", sdpString.length() ) ) );
            }
        }
        else if ( codec == AV_CODEC_ID_MPEG4 )
        {
            std::string encoderKey = MpegEncoder::getPoolKey( provider->identity(), width, height, frameRate, bitRate, quality );
            if ( !(mEncoder = Encoder::getPooledEncoder( encoderKey )) )
            {
                MpegEncoder *mpegEncoder = NULL;
                mEncoder = mpegEncoder = new MpegEncoder( provider->identity(), width, height, frameRate, bitRate, quality );
                mEncoder->registerProvider( *provider );
                Encoder::poolEncoder( mEncoder );
                mpegEncoder->start();
            }
            sdpString += mEncoder->sdpString( 1 ); // XXX - Should be variable
            responseHeaders.insert( Headers::value_type( "Content-length", stringtf( "%zd", sdpString.length() ) ) );
        }
        return( sendResponse( responseHeaders, sdpString ) );
    }
    else if ( requestType == "SETUP" )
    {
        // These commands are handled by RTSP session so pass them on and send any required responses
        RtspSession *rtspSession = 0;
        if ( session )
        {
            rtspSession = mRtspController->getSession( session );
        }
        else
        {
            rtspSession = mRtspController->newSession( this, mEncoder );
        }
        if ( rtspSession->recvRequest( requestType, requestUrl, requestHeaders, responseHeaders ) )
            return( sendResponse( responseHeaders ) );
        return( false );
    }
    else if ( requestType == "PLAY" || requestType == "GET_PARAMETER" || requestType == "TEARDOWN" )
    {
        // These commands are handled by RTSP session so pass them on and send any required responses
        RtspSession *rtspSession = 0;
        if ( session )
        {
            rtspSession = mRtspController->getSession( session );
            if ( rtspSession && rtspSession->recvRequest( requestType, requestUrl, requestHeaders, responseHeaders ) )
                return( sendResponse( responseHeaders ) );
        }
        return( sendResponse( responseHeaders, "", 454, "Session Not Found" ) );
    }
    Error( "Unrecognised RTSP command '%s'", requestType.c_str() );
    return( sendResponse( responseHeaders, "", 405, "Method not implemented" ) );
}
예제 #8
0
int RtspThread::run()
{
    std::string message;
    std::string response;

    response.reserve( ZM_NETWORK_BUFSIZ );

    if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
        Fatal( "Unable to connect RTSP socket" );
    //Select select( 0.25 );
    //select.addReader( &mRtspSocket );
    //while ( select.wait() )
    //{
        //mRtspSocket.recv( response );
        //Debug( 4, "Drained %d bytes from RTSP socket", response.size() );
    //}

    
    bool authTried = false; 
    if ( mMethod == RTP_RTSP_HTTP )
    {
        if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
            Fatal( "Unable to connect auxiliary RTSP/HTTP socket" );
        //Select select( 0.25 );
        //select.addReader( &mRtspSocket2 );
        //while ( select.wait() )
        //{
            //mRtspSocket2.recv( response );
            //Debug( 4, "Drained %d bytes from HTTP socket", response.size() );
        //}
        
        //possibly retry sending the message for authentication 
        int respCode = -1;
        char respText[256];
        do {
			message = "GET "+mPath+" HTTP/1.0\r\n";
			message += "X-SessionCookie: "+mHttpSession+"\r\n";
			if ( mNeedAuth ) {
				message += mAuthenticator->getAuthHeader("GET", mPath);
				authTried = true;
			}
		    message += "Accept: application/x-rtsp-tunnelled\r\n";
			message += "\r\n";
			Debug( 2, "Sending HTTP message: %s", message.c_str() );
			if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() )
			{
				Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
				return( -1 );
			}
			if ( mRtspSocket.recv( response ) < 0 )
			{
				Error( "Recv failed; %s", strerror(errno) );
				return( -1 );
			}
		
			Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() );
			float respVer = 0;
			respCode = -1;
			if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 )
			{
				if ( isalnum(response[0]) )
				{
				Error( "Response parse failure in '%s'", response.c_str() );
				}
				else
				{
				Error( "Response parse failure, %zd bytes follow", response.size() );
				if ( response.size() )
					Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
				}
				return( -1 );
			}
			// If Server requests authentication, check WWW-Authenticate header and fill required fields
			// for requested authentication method
			if (respCode == 401 && !authTried) {
				mNeedAuth = true;
				mAuthenticator->checkAuthResponse(response);
				Debug(2, "Processed 401 response");
				mRtspSocket.close();
			    if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
			        Fatal( "Unable to reconnect RTSP socket" );
			    Debug(2, "connection should be reopened now");
			}
			
		} while (respCode == 401 && !authTried);  
		
        if ( respCode != 200 )
        {
            Error( "Unexpected response code %d, text is '%s'", respCode, respText );
            return( -1 );
        }

        message = "POST "+mPath+" HTTP/1.0\r\n";
        message += "X-SessionCookie: "+mHttpSession+"\r\n";
		if ( mNeedAuth )
			message += mAuthenticator->getAuthHeader("POST", mPath);
        message += "Content-Length: 32767\r\n";
        message += "Content-Type: application/x-rtsp-tunnelled\r\n";
        message += "\r\n";
        Debug( 2, "Sending HTTP message: %s", message.c_str() );
        if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() )
        {
            Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
            return( -1 );
        }
    }

    std::string localHost = "";
    int localPorts[2] = { 0, 0 };

    // Request supported RTSP commands by the server
    message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
    if ( !sendCommand( message ) )
        return( -1 );

	// A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry.
    if ( !recvResponse( response ) ) {
		if ( mNeedAuth ) {
			Debug( 2, "Resending OPTIONS due to possible auth requirement" );
			if ( !sendCommand( message ) )
				return( -1 );
			if ( !recvResponse( response ) )
				return( -1 );
		} else {
			return( -1 );
		}
	} // end if failed response maybe due to auth

    char publicLine[256] = "";
    StringVector lines = split( response, "\r\n" );
    for ( size_t i = 0; i < lines.size(); i++ )
        sscanf( lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine );

    // Check if the server supports the GET_PARAMETER command
    // If yes, it is likely that the server will request this command as a keepalive message
    bool sendKeepalive = false;
    if ( publicLine[0] && strstr(publicLine, "GET_PARAMETER") )
        sendKeepalive = true;

    message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n";
	bool res;
	do {
		if (mNeedAuth)
			authTried = true;
		sendCommand( message );
		sleep( 1 );
		res = recvResponse( response );
		if (!res && respCode==401)
			mNeedAuth = true;
	} while (!res && respCode==401 && !authTried);

    const std::string endOfHeaders = "\r\n\r\n";
    size_t sdpStart = response.find( endOfHeaders );
    if( sdpStart == std::string::npos )
        return( -1 );

    std::string DescHeader = response.substr( 0,sdpStart );
    Debug( 1, "Processing DESCRIBE response header '%s'", DescHeader.c_str() );

    lines = split( DescHeader, "\r\n" );
    for ( size_t i = 0; i < lines.size(); i++ )
    	{
    		// If the device sends us a url value for Content-Base in the response header, we should use that instead
    		if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) )
    			{
    				mUrl = trimSpaces( lines[i].substr( 13 ) );
    				Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
    				break;
    			}
    	}

    sdpStart += endOfHeaders.length();

    std::string sdp = response.substr( sdpStart );
    Debug( 1, "Processing SDP '%s'", sdp.c_str() );

    try
    {
        mSessDesc = new SessionDescriptor( mUrl, sdp );
        mFormatContext = mSessDesc->generateFormatContext();
    }
    catch( const Exception &e )
    {
        Error( e.getMessage().c_str() );
        return( -1 );
    }

#if 0
    // New method using ffmpeg native functions
    std::string authUrl = mUrl;
    if ( !mAuth.empty() )
        authUrl.insert( authUrl.find( "://" )+3, mAuth+"@" );

    if ( av_open_input_file( &mFormatContext, authUrl.c_str(), NULL, 0, NULL ) != 0 )
    {
        Error( "Unable to open input '%s'", authUrl.c_str() );
        return( -1 );
    }
#endif

    uint32_t rtpClock = 0;
    std::string trackUrl = mUrl;
    std::string controlUrl;
    
    _AVCODECID codecId;
    
    if ( mFormatContext->nb_streams >= 1 )
    {
        for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
        {
            SessionDescriptor::MediaDescriptor *mediaDesc = mSessDesc->getStream( i );
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
            if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
#else
            if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
#endif
            {
                // Check if control Url is absolute or relative
                controlUrl = mediaDesc->getControlUrl();
                if (std::equal(trackUrl.begin(), trackUrl.end(), controlUrl.begin()))
                {
                    trackUrl = controlUrl;
                }
                else
                {
					if ( *trackUrl.rbegin() != '/') {
						trackUrl += "/" + controlUrl;
					} else {
						trackUrl += controlUrl;
					}
                }
                rtpClock = mediaDesc->getClock();
                codecId = mFormatContext->streams[i]->codec->codec_id;
                // Hackery pokery
                //rtpClock = mFormatContext->streams[i]->codec->sample_rate;
                break;
            }
        }
    }

    switch( mMethod )
    {
        case RTP_UNICAST :
        {
            localPorts[0] = requestPorts();
            localPorts[1] = localPorts[0]+1;

            message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port="+stringtf( "%d", localPorts[0] )+"-"+stringtf( "%d", localPorts[1] )+"\r\n";
            break;
        }
        case RTP_MULTICAST :
        {
            message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;multicast\r\n";
            break;
        }
        case RTP_RTSP :
        case RTP_RTSP_HTTP :
        {
            message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP/TCP;unicast\r\n";
            break;
        }
        default:
        {
            Panic( "Got unexpected method %d", mMethod );
            break;
        }
    }

    if ( !sendCommand( message ) )
        return( -1 );
    if ( !recvResponse( response ) )
        return( -1 );

    lines = split( response, "\r\n" );
    std::string session;
    int timeout = 0;
    char transport[256] = "";

    for ( size_t i = 0; i < lines.size(); i++ )
    {
        if ( ( lines[i].size() > 8 ) && ( lines[i].substr( 0, 8 ) == "Session:" ) )
        {
            StringVector sessionLine = split( lines[i].substr(9), ";" );
            session = trimSpaces( sessionLine[0] );
            if ( sessionLine.size() == 2 )
                sscanf( trimSpaces( sessionLine[1] ).c_str(), "timeout=%d", &timeout );
        }
        sscanf( lines[i].c_str(), "Transport: %s", transport );
    }

    if ( session.empty() )
        Fatal( "Unable to get session identifier from response '%s'", response.c_str() );

    Debug( 2, "Got RTSP session %s, timeout %d secs", session.c_str(), timeout );

    if ( !transport[0] )
        Fatal( "Unable to get transport details from response '%s'", response.c_str() );

    Debug( 2, "Got RTSP transport %s", transport );

    std::string method = "";
    int remotePorts[2] = { 0, 0 };
    int remoteChannels[2] = { 0, 0 };
    std::string distribution = "";
    unsigned long ssrc = 0;
    StringVector parts = split( transport, ";" );
    for ( size_t i = 0; i < parts.size(); i++ )
    {
        if ( parts[i] == "unicast" || parts[i] == "multicast" )
            distribution = parts[i];
        else if ( startsWith( parts[i], "server_port=" ) )
        {
            method = "RTP/UNICAST";
            StringVector subparts = split( parts[i], "=" );
            StringVector ports = split( subparts[1], "-" );
            remotePorts[0] = strtol( ports[0].c_str(), NULL, 10 );
            remotePorts[1] = strtol( ports[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "interleaved=" ) )
        {
            method = "RTP/RTSP";
            StringVector subparts = split( parts[i], "=" );
            StringVector channels = split( subparts[1], "-" );
            remoteChannels[0] = strtol( channels[0].c_str(), NULL, 10 );
            remoteChannels[1] = strtol( channels[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "port=" ) )
        {
            method = "RTP/MULTICAST";
            StringVector subparts = split( parts[i], "=" );
            StringVector ports = split( subparts[1], "-" );
            localPorts[0] = strtol( ports[0].c_str(), NULL, 10 );
            localPorts[1] = strtol( ports[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "destination=" ) )
        {
            StringVector subparts = split( parts[i], "=" );
            localHost = subparts[1];
        }
        else if ( startsWith( parts[i], "ssrc=" ) )
        {
            StringVector subparts = split( parts[i], "=" );
            ssrc = strtoll( subparts[1].c_str(), NULL, 16 );
        }
    }

    Debug( 2, "RTSP Method is %s", method.c_str() );
    Debug( 2, "RTSP Distribution is %s", distribution.c_str() );
    Debug( 2, "RTSP SSRC is %lx", ssrc );
    Debug( 2, "RTSP Local Host is %s", localHost.c_str() );
    Debug( 2, "RTSP Local Ports are %d/%d", localPorts[0], localPorts[1] );
    Debug( 2, "RTSP Remote Ports are %d/%d", remotePorts[0], remotePorts[1] );
    Debug( 2, "RTSP Remote Channels are %d/%d", remoteChannels[0], remoteChannels[1] );

    message = "PLAY "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\nRange: npt=0.000-\r\n";
    if ( !sendCommand( message ) )
        return( -1 );
    if ( !recvResponse( response ) )
        return( -1 );

    lines = split( response, "\r\n" );
    std::string rtpInfo;
    for ( size_t i = 0; i < lines.size(); i++ )
    {
        if ( ( lines[i].size() > 9 ) && ( lines[i].substr( 0, 9 ) == "RTP-Info:" ) )
            rtpInfo = trimSpaces( lines[i].substr( 9 ) );
	// Check for a timeout again. Some rtsp devices don't send a timeout until after the PLAY command is sent
        if ( ( lines[i].size() > 8 ) && ( lines[i].substr( 0, 8 ) == "Session:" ) && ( timeout == 0 ) )
        {
            StringVector sessionLine = split( lines[i].substr(9), ";" );
            if ( sessionLine.size() == 2 )
                sscanf( trimSpaces( sessionLine[1] ).c_str(), "timeout=%d", &timeout );
            if ( timeout > 0 )
                Debug( 2, "Got timeout %d secs from PLAY command response", timeout );
        }
    }

    int seq = 0;
    unsigned long rtpTime = 0;
    StringVector streams;
    if ( rtpInfo.empty() )
    {
        Debug( 1, "RTP Info Empty. Starting values for Sequence and Rtptime shall be zero.");
    }
    else
    {
        Debug( 2, "Got RTP Info %s", rtpInfo.c_str() );
        // More than one stream can be included in the RTP Info
        streams = split( rtpInfo.c_str(), "," );
        for ( size_t i = 0; i < streams.size(); i++ )
        {
            // We want the stream that matches the trackUrl we are using
            if ( streams[i].find(controlUrl.c_str()) != std::string::npos )
            {
                // Parse the sequence and rtptime values
                parts = split( streams[i].c_str(), ";" );
                for ( size_t j = 0; j < parts.size(); j++ )
                {
                    if ( startsWith( parts[j], "seq=" ) )
                    {
                        StringVector subparts = split( parts[j], "=" );
                        seq = strtol( subparts[1].c_str(), NULL, 10 );
                    }
                    else if ( startsWith( parts[j], "rtptime=" ) )
                    {
                        StringVector subparts = split( parts[j], "=" );
                        rtpTime = strtol( subparts[1].c_str(), NULL, 10 );
                    }
                }
            break;
            }
        }
    }

    Debug( 2, "RTSP Seq is %d", seq );
    Debug( 2, "RTSP Rtptime is %ld", rtpTime );

    time_t lastKeepalive = time(NULL);
	time_t now;
    message = "GET_PARAMETER "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";

    switch( mMethod )
    {
        case RTP_UNICAST :
        {
            RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId );
            mSources[ssrc] = source;
            RtpDataThread rtpDataThread( *this, *source );
            RtpCtrlThread rtpCtrlThread( *this, *source );

            rtpDataThread.start();
            rtpCtrlThread.start();

            while( !mStop )
            {
				now = time(NULL);
                // Send a keepalive message if the server supports this feature and we are close to the timeout expiration
Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
                if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) )
                {
                    if ( !sendCommand( message ) )
                        return( -1 );
                    lastKeepalive = now;
                }
                usleep( 100000 );
            }
#if 0
            message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );
#endif

            message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );

            rtpDataThread.stop();
            rtpCtrlThread.stop();

            //rtpDataThread.kill( SIGTERM );
            //rtpCtrlThread.kill( SIGTERM );

            rtpDataThread.join();
            rtpCtrlThread.join();
         
            delete mSources[ssrc];
            mSources.clear();

            releasePorts( localPorts[0] );

            break;
        }
        case RTP_RTSP :
        case RTP_RTSP_HTTP :
        {
            RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime, codecId );
            mSources[ssrc] = source;
            // These never actually run
            RtpDataThread rtpDataThread( *this, *source );
            RtpCtrlThread rtpCtrlThread( *this, *source );

            Select select( double(config.http_timeout)/1000.0 );
            select.addReader( &mRtspSocket );

            Buffer buffer( ZM_NETWORK_BUFSIZ );
            std::string keepaliveMessage = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
            std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n";
            while ( !mStop && select.wait() >= 0 )
            {
                Select::CommsList readable = select.getReadable();
                if ( readable.size() == 0 )
                {
                    Error( "RTSP timed out" );
                    break;
                }

                static char tempBuffer[ZM_NETWORK_BUFSIZ];
                ssize_t nBytes = mRtspSocket.recv( tempBuffer, sizeof(tempBuffer) );
                buffer.append( tempBuffer, nBytes );
                Debug( 4, "Read %zd bytes on sd %d, %d total", nBytes, mRtspSocket.getReadDesc(), buffer.size() );

                while( buffer.size() > 0 )
                {
                    if ( buffer[0] == '$' )
                    {
                	if ( buffer.size() < 4 )
                	    break;
                        unsigned char channel = buffer[1];
                        unsigned short len = ntohs( *((unsigned short *)(buffer+2)) );

                        Debug( 4, "Got %d bytes left, expecting %d byte packet on channel %d", buffer.size(), len, channel );
                        if ( (unsigned short)buffer.size() < (len+4) )
                        {
                            Debug( 4, "Missing %d bytes, rereading", (len+4)-buffer.size() );
                            break;
                        }
                        if ( channel == remoteChannels[0] )
                        {
                            Debug( 4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len );
                            Hexdump( 4, (char *)buffer, 16 );
                            rtpDataThread.recvPacket( buffer+4, len );
                            Debug( 4, "Received" );
                        }
                        else if ( channel == remoteChannels[1] )
                        {
//                            len = ntohs( *((unsigned short *)(buffer+2)) );
//                            Debug( 4, "Got %d bytes on control channel %d", nBytes, channel );
                            Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
                            Hexdump( 4, (char *)buffer, 16 );
                            rtpCtrlThread.recvPackets( buffer+4, len );
                        }
                        else
                        {
                            Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
                            buffer.clear();
                            break;
                        }
                        buffer.consume( len+4 );
                        nBytes -= len+4;
                    }
                    else
                    {
                        if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 )
                        {
                            Debug( 4, "Got keepalive response '%s'", (char *)buffer );
                            //buffer.consume( keepaliveResponse.size() );
                            if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
                            {
                                int discardBytes = charPtr-(char *)buffer;
                                buffer -= discardBytes;
                            }
                            else
                            {
                                buffer.clear();
                            }
                        }
                        else
                        {
                            if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
                            {
                                int discardBytes = charPtr-(char *)buffer;
                                Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes );
                                Hexdump( -1, (char *)buffer, discardBytes );
                                buffer -= discardBytes;
                            }
                            else
                            {
                                Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() );
                                Hexdump( -1, (char *)buffer, 32 );
                                buffer.clear();
                            }
                        }
                    }
                }
                // Send a keepalive message if the server supports this feature and we are close to the timeout expiration
                // FIXME: Is this really necessary when using tcp ?
				now = time(NULL);
                // Send a keepalive message if the server supports this feature and we are close to the timeout expiration
Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
                if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) )
                {
                    if ( !sendCommand( message ) )
                        return( -1 );
                    lastKeepalive = now;
                }
                buffer.tidy( 1 );
            }
#if 0
            message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );
#endif
            // Send a teardown message but don't expect a response as this may not be implemented on the server when using TCP
            message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );

            delete mSources[ssrc];
            mSources.clear();

            break;
        }
        case RTP_MULTICAST :
        {
            RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId );
            mSources[ssrc] = source;
            RtpDataThread rtpDataThread( *this, *source );
            RtpCtrlThread rtpCtrlThread( *this, *source );

            rtpDataThread.start();
            rtpCtrlThread.start();

            while( !mStop )
            {
                // Send a keepalive message if the server supports this feature and we are close to the timeout expiration
                if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) )
                {
                    if ( !sendCommand( message ) )
                        return( -1 );
                    lastKeepalive = time(NULL);
                }
                usleep( 100000 );
            }
#if 0
            message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );
#endif
            message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );

            rtpDataThread.stop();
            rtpCtrlThread.stop();

            rtpDataThread.join();
            rtpCtrlThread.join();
         
            delete mSources[ssrc];
            mSources.clear();

            releasePorts( localPorts[0] );
            break;
        }
        default:
        {
            Panic( "Got unexpected method %d", mMethod );
            break;
        }
    }

    return( 0 );
}
예제 #9
0
void zmLoadConfig()
{
  FILE *cfg;
  char line[512];
  if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
  {
    Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
  }
  while ( fgets( line, sizeof(line), cfg ) != NULL )
  {
    char *line_ptr = line;

    // Trim off any cr/lf line endings
    int chomp_len = strcspn( line_ptr, "\r\n" );
    line_ptr[chomp_len] = '\0';

    // Remove leading white space
    int white_len = strspn( line_ptr, " \t" );
    line_ptr += white_len;

    // Check for comment or empty line
    if ( *line_ptr == '\0' || *line_ptr == '#' )
      continue;

    // Remove trailing white space
    char *temp_ptr = line_ptr+strlen(line_ptr)-1;
    while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
    {
      *temp_ptr-- = '\0';
      temp_ptr--;
    }

    // Now look for the '=' in the middle of the line
    temp_ptr = strchr( line_ptr, '=' );
    if ( !temp_ptr )
    {
      Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
      continue;
    }

    // Assign the name and value parts
    char *name_ptr = line_ptr;
    char *val_ptr = temp_ptr+1;

    // Trim trailing space from the name part
    do
    {
      *temp_ptr = '\0';
      temp_ptr--;
    }
    while ( *temp_ptr == ' ' || *temp_ptr == '\t' );

    // Remove leading white space from the value part
    white_len = strspn( val_ptr, " \t" );
    val_ptr += white_len;

    if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
      staticConfig.DB_HOST = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
      staticConfig.DB_NAME = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
      staticConfig.DB_USER = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
      staticConfig.DB_PASS = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
      staticConfig.PATH_WEB = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
      staticConfig.SERVER_NAME = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 )
      staticConfig.SERVER_NAME = std::string(val_ptr);
    else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
      staticConfig.SERVER_ID = atoi(val_ptr);
    else
    {
      // We ignore this now as there may be more parameters than the
      // c/c++ binaries are bothered about
      // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
    }
  } // end foreach line of the config
  fclose( cfg );
  zmDbConnect();
  config.Load();
  config.Assign();

  // Populate the server config entries
  if ( ! staticConfig.SERVER_ID ) {
    if ( ! staticConfig.SERVER_NAME.empty() ) {

      Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
      std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
      if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
        staticConfig.SERVER_ID = atoi(dbrow[0]);
      } else {
        Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
      }

    } // end if has SERVER_NAME
  } else if ( staticConfig.SERVER_NAME.empty() ) {
    Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
    std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
    
    if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
      staticConfig.SERVER_NAME = std::string(dbrow[0]);
    } else {
      Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
    }
  
  }  
  if ( ! staticConfig.SERVER_ID ) {
    Debug( 1, "No Server ID or Name specified in config.  Not using Multi-Server Mode." );
  } else {
    Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
  }
}
예제 #10
0
/**
* @brief 
*
* @param name
* @param width
* @param height
* @param frameRate
* @param bitRate
* @param quality
*
* @return 
*/
std::string H264Encoder::getPoolKey( const std::string &name, uint16_t width, uint16_t height, FrameRate frameRate, uint32_t bitRate, uint8_t quality )
{
    return( stringtf( "%s-h264-%dx%d@%d/%d-%d(%d)", name.c_str(), width, height, frameRate.num, frameRate.den, bitRate, quality ) );
}
예제 #11
0
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
{
  if ( pthread_cond_init( &mCondition, NULL ) < 0 )
    throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
}
예제 #12
0
int RtspThread::run()
{
    std::string message;
    std::string response;

    response.reserve( ZM_NETWORK_BUFSIZ );

    if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
        Fatal( "Unable to connect RTSP socket" );
    //Select select( 0.25 );
    //select.addReader( &mRtspSocket );
    //while ( select.wait() )
    //{
        //mRtspSocket.recv( response );
        //Debug( 4, "Drained %d bytes from RTSP socket", response.size() );
    //}

    if ( mMethod == RTP_RTSP_HTTP )
    {
        if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) )
            Fatal( "Unable to connect auxiliary RTSP/HTTP socket" );
        //Select select( 0.25 );
        //select.addReader( &mRtspSocket2 );
        //while ( select.wait() )
        //{
            //mRtspSocket2.recv( response );
            //Debug( 4, "Drained %d bytes from HTTP socket", response.size() );
        //}

        message = "GET "+mPath+" HTTP/1.0\r\n";
        message += "X-SessionCookie: "+mHttpSession+"\r\n";
        if ( !mAuth.empty() )
            message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() );
        message += "\r\n";
        Debug( 2, "Sending HTTP message: %s", message.c_str() );
        if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() )
        {
            Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
            return( -1 );
        }
        if ( mRtspSocket.recv( response ) < 0 )
        {
            Error( "Recv failed; %s", strerror(errno) );
            return( -1 );
        }

        Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() );
        float respVer = 0;
        int respCode = -1;
        char respText[256];
        if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 )
        {
            if ( isalnum(response[0]) )
            {
                Error( "Response parse failure in '%s'", response.c_str() );
            }
            else
            {
                Error( "Response parse failure, %zd bytes follow", response.size() );
                if ( response.size() )
                    Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
            }
            return( -1 );
        }
        if ( respCode != 200 )
        {
            Error( "Unexpected response code %d, text is '%s'", respCode, respText );
            return( -1 );
        }

        message = "POST "+mPath+" HTTP/1.0\r\n";
        message += "X-SessionCookie: "+mHttpSession+"\r\n";
        if ( !mAuth.empty() )
            message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() );
        message += "Content-Length: 32767\r\n";
        message += "Content-Type: application/x-rtsp-tunnelled\r\n";
        message += "\r\n";
        Debug( 2, "Sending HTTP message: %s", message.c_str() );
        if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() )
        {
            Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
            return( -1 );
        }
    }

    std::string localHost = "";
    int localPorts[2] = { 0, 0 };

    //message = "OPTIONS * RTSP/1.0\r\n";
    //sendCommand( message );
    //recvResponse( response );

    message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n";
    sendCommand( message );
    sleep( 1 );
    recvResponse( response );

    const std::string endOfHeaders = "\r\n\r\n";
    size_t sdpStart = response.find( endOfHeaders );
    if( sdpStart == std::string::npos )
        return( -1 );
    sdpStart += endOfHeaders.length();

    std::string sdp = response.substr( sdpStart );
    Debug( 1, "Processing SDP '%s'", sdp.c_str() );

    SessionDescriptor *sessDesc = 0;
    try
    {
        sessDesc = new SessionDescriptor( mUrl, sdp );
        mFormatContext = sessDesc->generateFormatContext();
    }
    catch( const Exception &e )
    {
        Error( e.getMessage().c_str() );
        return( -1 );
    }

#if 0
    // New method using ffmpeg native functions
    std::string authUrl = mUrl;
    if ( !mAuth.empty() )
        authUrl.insert( authUrl.find( "://" )+3, mAuth+"@" );

    if ( av_open_input_file( &mFormatContext, authUrl.c_str(), NULL, 0, NULL ) != 0 )
    {
        Error( "Unable to open input '%s'", authUrl.c_str() );
        return( -1 );
    }
#endif

    uint32_t rtpClock = 0;
    std::string trackUrl = mUrl;
    
    #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0)
    enum AVCodecID codecId;
    #else
    enum CodecID codecId;
    #endif
    
    if ( mFormatContext->nb_streams >= 1 )
    {
        for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
        {
            SessionDescriptor::MediaDescriptor *mediaDesc = sessDesc->getStream( i );
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1)
            if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
#else
            if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
#endif
            {
                trackUrl += "/"+mediaDesc->getControlUrl();
                rtpClock = mediaDesc->getClock();
                codecId = mFormatContext->streams[i]->codec->codec_id;
                // Hackery pokery
                //rtpClock = mFormatContext->streams[i]->codec->sample_rate;
                break;
            }
        }
    }

    switch( mMethod )
    {
        case RTP_UNICAST :
        {
            localPorts[0] = requestPorts();
            localPorts[1] = localPorts[0]+1;

            message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port="+stringtf( "%d", localPorts[0] )+"-"+stringtf( "%d", localPorts[1] )+"\r\n";
            break;
        }
        case RTP_MULTICAST :
        {
            message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;multicast\r\n";
            break;
        }
        case RTP_RTSP :
        case RTP_RTSP_HTTP :
        {
            message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP/TCP;unicast\r\n";
            break;
        }
        default:
        {
            Panic( "Got unexpected method %d", mMethod );
            break;
        }
    }

    if ( !sendCommand( message ) )
        return( -1 );
    if ( !recvResponse( response ) )
        return( -1 );

    StringVector lines = split( response, "\r\n" );
    char *session = 0;
    int timeout = 0;
    char transport[256] = "";

    for ( size_t i = 0; i < lines.size(); i++ )
    {
        sscanf( lines[i].c_str(), "Session: %a[0-9a-fA-F]; timeout=%d", &session, &timeout );
        sscanf( lines[i].c_str(), "Transport: %s", transport );
    }

    if ( !session )
        Fatal( "Unable to get session identifier from response '%s'", response.c_str() );

    Debug( 2, "Got RTSP session %s, timeout %d secs", session, timeout );

    if ( !transport[0] )
        Fatal( "Unable to get transport details from response '%s'", response.c_str() );

    Debug( 2, "Got RTSP transport %s", transport );

    std::string method = "";
    int remotePorts[2] = { 0, 0 };
    int remoteChannels[2] = { 0, 0 };
    std::string distribution = "";
    unsigned long ssrc = 0;
    StringVector parts = split( transport, ";" );
    for ( size_t i = 0; i < parts.size(); i++ )
    {
        if ( parts[i] == "unicast" || parts[i] == "multicast" )
            distribution = parts[i];
        else if ( startsWith( parts[i], "server_port=" ) )
        {
            method = "RTP/UNICAST";
            StringVector subparts = split( parts[i], "=" );
            StringVector ports = split( subparts[1], "-" );
            remotePorts[0] = strtol( ports[0].c_str(), NULL, 10 );
            remotePorts[1] = strtol( ports[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "interleaved=" ) )
        {
            method = "RTP/RTSP";
            StringVector subparts = split( parts[i], "=" );
            StringVector channels = split( subparts[1], "-" );
            remoteChannels[0] = strtol( channels[0].c_str(), NULL, 10 );
            remoteChannels[1] = strtol( channels[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "port=" ) )
        {
            method = "RTP/MULTICAST";
            StringVector subparts = split( parts[i], "=" );
            StringVector ports = split( subparts[1], "-" );
            localPorts[0] = strtol( ports[0].c_str(), NULL, 10 );
            localPorts[1] = strtol( ports[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "destination=" ) )
        {
            StringVector subparts = split( parts[i], "=" );
            localHost = subparts[1];
        }
        else if ( startsWith( parts[i], "ssrc=" ) )
        {
            StringVector subparts = split( parts[i], "=" );
            ssrc = strtoll( subparts[1].c_str(), NULL, 16 );
        }
    }

    Debug( 2, "RTSP Method is %s", method.c_str() );
    Debug( 2, "RTSP Distribution is %s", distribution.c_str() );
    Debug( 2, "RTSP SSRC is %lx", ssrc );
    Debug( 2, "RTSP Local Host is %s", localHost.c_str() );
    Debug( 2, "RTSP Local Ports are %d/%d", localPorts[0], localPorts[1] );
    Debug( 2, "RTSP Remote Ports are %d/%d", remotePorts[0], remotePorts[1] );
    Debug( 2, "RTSP Remote Channels are %d/%d", remoteChannels[0], remoteChannels[1] );

    message = "PLAY "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\nRange: npt=0.000-\r\n";
    if ( !sendCommand( message ) )
        return( -1 );
    if ( !recvResponse( response ) )
        return( -1 );

    lines = split( response, "\r\n" );
    char *rtpInfo = 0;
    for ( size_t i = 0; i < lines.size(); i++ )
    {
        sscanf( lines[i].c_str(), "RTP-Info: %as", &rtpInfo );
    }

    if ( !rtpInfo )
        Fatal( "Unable to get RTP Info identifier from response '%s'", response.c_str() );

    Debug( 2, "Got RTP Info %s", rtpInfo );

    int seq = 0;
    unsigned long rtpTime = 0;
    parts = split( rtpInfo, ";" );
    for ( size_t i = 0; i < parts.size(); i++ )
    {
        if ( startsWith( parts[i], "seq=" ) )
        {
            StringVector subparts = split( parts[i], "=" );
            seq = strtol( subparts[1].c_str(), NULL, 10 );
        }
        else if ( startsWith( parts[i], "rtptime=" ) )
        {
            StringVector subparts = split( parts[i], "=" );
            rtpTime = strtol( subparts[1].c_str(), NULL, 10 );
        }
    }

    Debug( 2, "RTSP Seq is %d", seq );
    Debug( 2, "RTSP Rtptime is %ld", rtpTime );

    switch( mMethod )
    {
        case RTP_UNICAST :
        {
            RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId );
            mSources[ssrc] = source;
            RtpDataThread rtpDataThread( *this, *source );
            RtpCtrlThread rtpCtrlThread( *this, *source );

            rtpDataThread.start();
            rtpCtrlThread.start();

            while( !mStop )
            {
                usleep( 100000 );
            }
#if 0
            message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );
#endif

            message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );

            rtpDataThread.stop();
            rtpCtrlThread.stop();

            //rtpDataThread.kill( SIGTERM );
            //rtpCtrlThread.kill( SIGTERM );

            rtpDataThread.join();
            rtpCtrlThread.join();
         
            delete mSources[ssrc];
            mSources.clear();

            releasePorts( localPorts[0] );

            break;
        }
        case RTP_RTSP :
        case RTP_RTSP_HTTP :
        {
            RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime, codecId );
            mSources[ssrc] = source;
            // These never actually run
            RtpDataThread rtpDataThread( *this, *source );
            RtpCtrlThread rtpCtrlThread( *this, *source );

            Select select( double(config.http_timeout)/1000.0 );
            select.addReader( &mRtspSocket );

            Buffer buffer( ZM_NETWORK_BUFSIZ );
            time_t lastKeepalive = time(NULL);
            std::string keepaliveMessage = "OPTIONS * RTSP/1.0\r\n";
            std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n";
            while ( !mStop && select.wait() >= 0 )
            {
                Select::CommsList readable = select.getReadable();
                if ( readable.size() == 0 )
                {
                    Error( "RTSP timed out" );
                    break;
                }

                static char tempBuffer[ZM_NETWORK_BUFSIZ];
                ssize_t nBytes = mRtspSocket.recv( tempBuffer, sizeof(tempBuffer) );
                buffer.append( tempBuffer, nBytes );
                Debug( 4, "Read %zd bytes on sd %d, %d total", nBytes, mRtspSocket.getReadDesc(), buffer.size() );

                while( buffer.size() > 0 )
                {
                    if ( buffer[0] == '$' )
                    {
                	if ( buffer.size() < 4 )
                	    break;
                        unsigned char channel = buffer[1];
                        unsigned short len = ntohs( *((unsigned short *)(buffer+2)) );

                        Debug( 4, "Got %d bytes left, expecting %d byte packet on channel %d", buffer.size(), len, channel );
                        if ( (unsigned short)buffer.size() < (len+4) )
                        {
                            Debug( 4, "Missing %d bytes, rereading", (len+4)-buffer.size() );
                            break;
                        }
                        if ( channel == remoteChannels[0] )
                        {
                            Debug( 4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len );
                            Hexdump( 4, (char *)buffer, 16 );
                            rtpDataThread.recvPacket( buffer+4, len );
                            Debug( 4, "Received" );
                        }
                        else if ( channel == remoteChannels[1] )
                        {
//                            len = ntohs( *((unsigned short *)(buffer+2)) );
//                            Debug( 4, "Got %d bytes on control channel %d", nBytes, channel );
                            Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
                            Hexdump( 4, (char *)buffer, 16 );
                            rtpCtrlThread.recvPackets( buffer+4, len );
                        }
                        else
                        {
                            Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
                            buffer.clear();
                            break;
                        }
                        buffer.consume( len+4 );
                        nBytes -= len+4;
                    }
                    else
                    {
                        if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 )
                        {
                            Debug( 4, "Got keepalive response '%s'", (char *)buffer );
                            //buffer.consume( keepaliveResponse.size() );
                            if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
                            {
                                int discardBytes = charPtr-(char *)buffer;
                                buffer -= discardBytes;
                            }
                            else
                            {
                                buffer.clear();
                            }
                        }
                        else
                        {
                            if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
                            {
                                int discardBytes = charPtr-(char *)buffer;
                                Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes );
                                Hexdump( -1, (char *)buffer, discardBytes );
                                buffer -= discardBytes;
                            }
                            else
                            {
                                Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() );
                                Hexdump( -1, (char *)buffer, 32 );
                                buffer.clear();
                            }
                        }
                    }
                }
                if ( (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) )
                {
                    if ( !sendCommand( message ) )
                        return( -1 );
                    lastKeepalive = time(NULL);
                }
                buffer.tidy( 1 );
            }
#if 0
            message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );
#endif
            message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );

            delete mSources[ssrc];
            mSources.clear();

            break;
        }
        case RTP_MULTICAST :
        {
            RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId );
            mSources[ssrc] = source;
            RtpDataThread rtpDataThread( *this, *source );
            RtpCtrlThread rtpCtrlThread( *this, *source );

            rtpDataThread.start();
            rtpCtrlThread.start();

            while( !mStop )
            {
                usleep( 100000 );
            }
#if 0
            message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );
#endif
            message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
            if ( !sendCommand( message ) )
                return( -1 );
            if ( !recvResponse( response ) )
                return( -1 );

            rtpDataThread.stop();
            rtpCtrlThread.stop();

            rtpDataThread.join();
            rtpCtrlThread.join();
         
            delete mSources[ssrc];
            mSources.clear();

            releasePorts( localPorts[0] );
            break;
        }
        default:
        {
            Panic( "Got unexpected method %d", mMethod );
            break;
        }
    }

    return( 0 );
}
예제 #13
0
/**
* @brief 
*
* @param outputFormat
*
* @return 
*/
AVFormatContext *Mp4FileOutput::openFile( AVOutputFormat *outputFormat )
{
    /* allocate the output media context */
    AVFormatContext *outputContext = avformat_alloc_context();
    if ( !outputContext )
        Fatal( "Unable to allocate output context" );
    outputContext->oformat = outputFormat;

    AVDictionary *opts = NULL;
    //av_dict_set( &opts, "width", "352", 0 );
    //av_dict_set( &opts, "height", "288", 0 );
    //av_dict_set( &opts, "vpre", "medium", 0 );

    av_dict_set( &outputContext->metadata, "author", "Pontis EMC Systems", 0 );
    av_dict_set( &outputContext->metadata, "comment", "PECOS Footage", 0 );
    //av_dict_set( &outputContext->metadata, "copyright", "COPYRIGHT", 0 );
    av_dict_set( &outputContext->metadata, "title", "TITLE", 0 );

    // x264 Baseline
    //avSetH264Preset( &opts, "medium" );
    //av_dict_set( &opts, "b", "200k", 0 );
    //av_dict_set( &opts, "bt", "240k", 0 );

    avDumpDict( opts );

    /* add the audio and video streams using the default format codecs and initialize the codecs */
    AVStream *videoStream = NULL;
    AVCodecContext *videoCodecContext = NULL;

    videoStream = avformat_new_stream( outputContext, NULL );
    if ( !videoStream )
        Fatal( "Could not alloc video stream" );

    videoCodecContext = videoStream->codec;
    videoCodecContext->codec_id = outputFormat->video_codec;
    videoCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;

    videoCodecContext->width = mVideoParms.width();
    videoCodecContext->height = mVideoParms.height();
    videoCodecContext->time_base = mVideoParms.frameRate();
    //videoCodecContext->time_base.num = 1;
    //videoCodecContext->time_base.den = 90000;
    //videoCodecContext->pix_fmt = mVideoParms.pixelFormat();

    // some formats want stream headers to be separate
    if ( outputContext->oformat->flags & AVFMT_GLOBALHEADER )
        videoCodecContext->flags |= CODEC_FLAG_GLOBAL_HEADER;

    //av_dump_format( outputContext, 0, filename, 1 );

    time_t now = time( NULL );
    std::string filename = stringtf( "%s/%s-%ld.%s", mLocation.c_str(), cname(), now, mExtension.c_str() );
    snprintf( outputContext->filename, sizeof(outputContext->filename), "%s", filename.c_str() );
    Info( "Writing to movie file '%s'", outputContext->filename );

    if ( avio_open( &outputContext->pb, filename.c_str(), AVIO_FLAG_WRITE ) < 0 )
        Fatal( "Could not open output filename '%s'", filename.c_str() );

    /* write the stream header, if any */
    avformat_write_header( outputContext, &opts );
    avDumpDict( opts );

    return( outputContext );
}
예제 #14
0
/**
* @brief 
*
* @return 
*/
int Mp4FileOutput::run()
{
    //const int MAX_EVENT_HEAD_AGE = 2;    ///< Number of seconds of video before event to save
    const int MAX_EVENT_TAIL_AGE = 3;    ///< Number of seconds of video after event to save

    typedef enum { IDLE, PREALARM, ALARM, ALERT } AlarmState;

    if ( waitForProviders() )
    {
        /* auto detect the output format from the name. default is mpeg. */
        AVOutputFormat *outputFormat = av_guess_format( mExtension.c_str(), NULL, NULL );
        if ( !outputFormat )
            Fatal( "Could not deduce output format from '%s'", mExtension.c_str() );
        //AVFormatContext *outputContext = openFile( outputFormat );
        AVFormatContext *outputContext = NULL;

        double videoTimeOffset = 0.0L;
        uint64_t videoFrameCount = 0;
        AlarmState alarmState = IDLE;
        uint64_t alarmTime = 0;
        int eventCount = 0;
        while( !mStop )
        {
            while( !mStop )
            {
                mQueueMutex.lock();
                if ( !mFrameQueue.empty() )
                {
                    for ( FrameQueue::iterator iter = mFrameQueue.begin(); iter != mFrameQueue.end(); iter++ )
                    {
                        const FeedFrame *frame = iter->get();

                        Debug( 3, "Frame type %d", frame->mediaType() );
                        if ( frame->mediaType() == FeedFrame::FRAME_TYPE_VIDEO )
                        {
                        	// This is an alarm detection frame
                            const MotionFrame *motionFrame = dynamic_cast<const MotionFrame *>(frame);
                            //const VideoProvider *provider = dynamic_cast<const VideoProvider *>(frame->provider());

                            AlarmState lastAlarmState = alarmState;
                            uint64_t now = time64();

                            Debug( 3, "Motion frame, alarmed %d", motionFrame->alarmed() );
                            if ( motionFrame->alarmed() )
                            {
                                alarmState = ALARM;
                                alarmTime = now;
                                if ( lastAlarmState == IDLE )
                                {
                                    // Create new event
                                    eventCount++;
                                    std::string path = stringtf( "%s/img-%s-%d-%ju.jpg", mLocation.c_str(), mName.c_str(), eventCount, motionFrame->id() );
                                    //Info( "PF:%d @ %dx%d", motionFrame->pixelFormat(), motionFrame->width(), motionFrame->height() );
                                    Image image( motionFrame->pixelFormat(), motionFrame->width(), motionFrame->height(), motionFrame->buffer().data() );
                                    image.writeJpeg( path.c_str() );
                                }
                            }
                            else if ( lastAlarmState == ALARM )
                            {
                                alarmState = ALERT;
                            }
                            else if ( lastAlarmState == ALERT )
                            {
                            	Debug( 3, "Frame age %.2lf", frame->age( alarmTime ) );
                                if ( (0.0l-frame->age( alarmTime )) > MAX_EVENT_TAIL_AGE )
                                    alarmState = IDLE;
                            }
                            else
                            {
                            	alarmState = IDLE;
                            }
                            Debug( 3, "Alarm state %d (%d)", alarmState, lastAlarmState );
                        }
                        else
                        {
							bool keyFrame = false;
							const uint8_t *startPos = h264StartCode( frame->buffer().head(), frame->buffer().tail() );
							while ( startPos < frame->buffer().tail() )
							{
								while( !*(startPos++) )
									;
								const uint8_t *nextStartPos = h264StartCode( startPos, frame->buffer().tail() );

								int frameSize = nextStartPos-startPos;

								unsigned char type = startPos[0] & 0x1F;
								unsigned char nri = startPos[0] & 0x60;
								Debug( 3, "Frame Type %d, NRI %d (%02x), %d bytes, ts %jd", type, nri>>5, startPos[0], frameSize, frame->timestamp() );

								if ( type == NAL_IDR_SLICE )
									keyFrame = true;
								startPos = nextStartPos;
							}

							videoTimeOffset += (double)mVideoParms.frameRate().num / mVideoParms.frameRate().den;

							if ( keyFrame )
							{
								// We can do file opening/closing now
								if ( alarmState != IDLE && !outputContext )
								{
									outputContext = openFile( outputFormat );
									videoTimeOffset = 0.0L;
									videoFrameCount = 0;
								}
								else if ( alarmState == IDLE && outputContext )
								{
									closeFile( outputContext );
									outputContext = NULL;
								}
							}
							/*if ( keyFrame && (videoTimeOffset >= mMaxLength) )
							{
								closeFile( outputContext );
								outputContext = openFile( outputFormat );
								videoTimeOffset = 0.0L;
								videoFrameCount = 0;
							}*/

							if ( outputContext )
							{
								AVStream *videoStream = outputContext->streams[0];
								AVCodecContext *videoCodecContext = videoStream->codec;

								AVPacket packet;
								av_init_packet(&packet);

								packet.flags |= keyFrame ? AV_PKT_FLAG_KEY : 0;
								packet.stream_index = videoStream->index;
								packet.data = (uint8_t*)frame->buffer().data();
								packet.size = frame->buffer().size();
								//packet.pts = packet.dts = AV_NOPTS_VALUE;
								packet.pts = packet.dts = (videoFrameCount * mVideoParms.frameRate().num * videoCodecContext->time_base.den) / (mVideoParms.frameRate().den * videoCodecContext->time_base.num);
								Info( "vfc: %ju, vto: %.2lf, kf: %d, pts: %jd", videoFrameCount, videoTimeOffset, keyFrame, packet.pts );

								int result = av_interleaved_write_frame(outputContext, &packet);
								if ( result != 0 )
									Fatal( "Error while writing video frame: %d", result );
							}

							videoFrameCount++;
                        }
                    }
                    mFrameQueue.clear();
                }
                mQueueMutex.unlock();
                checkProviders();
                usleep( INTERFRAME_TIMEOUT );
            }
        }
        if ( outputContext )
        	closeFile( outputContext );
    }
    cleanup();
    return 0;
}
예제 #15
0
void MonitorStream::runStream() {
  if ( type == STREAM_SINGLE ) {
    // Not yet migrated over to stream class
    SingleImage(scale);
    return;
  }

  openComms();

  if ( !checkInitialised() ) {
    Error("Not initialized");
    return;
  }

  updateFrameRate(monitor->GetFPS());

  if ( type == STREAM_JPEG )
    fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout);

  // point to end which is theoretically not a valid value because all indexes are % image_buffer_count
  unsigned int last_read_index = monitor->image_buffer_count; 

  time_t stream_start_time;
  time(&stream_start_time);

  frame_count = 0;

  temp_image_buffer = 0;
  temp_image_buffer_count = playback_buffer;
  temp_read_index = temp_image_buffer_count;
  temp_write_index = temp_image_buffer_count;

  std::string swap_path;
  bool buffered_playback = false;

  // Last image and timestamp when paused, will be resent occasionally to prevent timeout
  Image *paused_image = NULL;
  struct timeval paused_timestamp;

  // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
  const int max_swap_len_suffix = 15; 

  int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator
  int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1;
  int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
  int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;

  if ( connkey && ( playback_buffer > 0 ) ) {

    if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
      Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
    } else {
      swap_path = staticConfig.PATH_SWAP;

      Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
      if ( checkSwapPath(swap_path.c_str(), true) ) {
        swap_path += stringtf("/zmswap-m%d", monitor->Id());

        Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
        if ( checkSwapPath(swap_path.c_str(), true) ) {
          swap_path += stringtf("/zmswap-q%06d", connkey);

          Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
          if ( checkSwapPath(swap_path.c_str(), true) ) {
            buffered_playback = true;
          }
        }
      }

      if ( !buffered_playback ) {
        Error("Unable to validate swap image path, disabling buffered playback");
      } else {
        Debug(2, "Assigning temporary buffer");
        temp_image_buffer = new SwapImage[temp_image_buffer_count];
        memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count );
        Debug( 2, "Assigned temporary buffer" );
      }
    }
  } else {
    Debug(2, "Not using playback_buffer");
  } // end if connkey  & playback_buffer

  float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs)
  while ( !zm_terminate ) {
    bool got_command = false;
    if ( feof(stdout) ) {
      Debug(2,"feof stdout");
      break;
    } else if ( ferror(stdout) ) {
      Debug(2,"ferror stdout");
      break;
    } else if ( !monitor->ShmValid() ) {
      Debug(2,"monitor not valid.... maybe we should wait until it comes back.");
      break;
    }

    gettimeofday(&now, NULL);

    bool was_paused = paused;
    if ( connkey ) {
      while(checkCommandQueue()) {
Debug(2, "Have checking command Queue for connkey: %d", connkey );
        got_command = true;
      }
      // Update modified time of the socket .lock file so that we can tell which ones are stale.
      if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
        touch(sock_path_lock);
        last_comm_update = now;
      }

    }

    if ( paused ) {
      if ( !was_paused ) {
        int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
        Debug(1,"Saving paused image from index %d",index);
        paused_image = new Image( *monitor->image_buffer[index].image );
        paused_timestamp = *(monitor->image_buffer[index].timestamp);
      }
    } else if ( paused_image ) {
      Debug(1,"Clearing paused_image");
      delete paused_image;
      paused_image = NULL;
    }

    if ( buffered_playback && delayed ) {
      if ( temp_read_index == temp_write_index ) {
        // Go back to live viewing
        Debug( 1, "Exceeded temporary streaming buffer" );
        // Clear paused flag
        paused = false;
        // Clear delayed_play flag
        delayed = false;
        replay_rate = ZM_RATE_BASE;
      } else {
        if ( !paused ) {
          int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
          //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
          SwapImage *swap_image = &temp_image_buffer[temp_index];

          if ( !swap_image->valid ) {
            paused = true;
            delayed = true;
            temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
          } else {
            //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) );
            double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate;
            double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;

            //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) );
            // If the next frame is due
            if ( actual_delta_time > expected_delta_time ) {
              //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
              if ( temp_index%frame_mod == 0 ) {
                Debug( 2, "Sending delayed frame %d", temp_index );
                // Send the next frame
                if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) )
                  zm_terminate = true;
                memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
                //frame_sent = true;
              }
              temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
            }
          }
        } else if ( step != 0 ) {
          temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count );

          SwapImage *swap_image = &temp_image_buffer[temp_read_index];

          // Send the next frame
          if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) )
            zm_terminate = true;
          memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) );
          //frame_sent = true;
          step = 0;
        } else {
          //paused?
          int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);

           double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
           if ( got_command || actual_delta_time > 5 ) {
            // Send keepalive
            Debug( 2, "Sending keepalive frame %d", temp_index );
            // Send the next frame
            if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) )
              zm_terminate = true;
            //frame_sent = true;
          }
        } // end if (!paused) or step or paused
      } // end if have exceeded buffer or not

      if ( temp_read_index == temp_write_index ) {
        // Go back to live viewing
        Warning( "Rewound over write index, resuming live play" );
        // Clear paused flag
        paused = false;
        // Clear delayed_play flag
        delayed = false;
        replay_rate = ZM_RATE_BASE;
      }
    } // end if ( buffered_playback && delayed )

    if ( last_read_index != monitor->shared_data->last_write_index ) {
      // have a new image to send
      int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
      last_read_index = monitor->shared_data->last_write_index;
      Debug( 2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", index, frame_mod, frame_count, paused, delayed );
      if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
        if ( !paused && !delayed ) {
          // Send the next frame
          Monitor::Snapshot *snap = &monitor->image_buffer[index];

          Debug(2, "sending Frame.");
          if ( !sendFrame(snap->image, snap->timestamp) ) {
            Debug(2, "sendFrame failed, quiting.");
            zm_terminate = true;
          }
          // Perhaps we should use NOW instead. 
          memcpy(&last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp));
          //frame_sent = true;

          temp_read_index = temp_write_index;
        } else {
          double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
          if ( actual_delta_time > 5 ) {
            if ( paused_image ) {
              // Send keepalive
              Debug(2, "Sending keepalive frame ");
              // Send the next frame
              if ( !sendFrame(paused_image, &paused_timestamp) )
                zm_terminate = true;
            } else {
              Debug(2, "Would have sent keepalive frame, but had no paused_image ");
            }
           }
        }
      } // end if should send frame

      if ( buffered_playback && !paused ) {
        if ( monitor->shared_data->valid ) {
          if ( monitor->image_buffer[index].timestamp->tv_sec ) {
            int temp_index = temp_write_index%temp_image_buffer_count;
            Debug(2, "Storing frame %d", temp_index);
            if ( !temp_image_buffer[temp_index].valid ) {
              snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path.c_str(), temp_index );
              temp_image_buffer[temp_index].valid = true;
            }
            memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) );
            monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality );
            temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count );
            if ( temp_write_index == temp_read_index ) {
              // Go back to live viewing
              Warning( "Exceeded temporary buffer, resuming live play" );
              paused = false;
              delayed = false;
              replay_rate = ZM_RATE_BASE;
            }
          } else {
            Warning( "Unable to store frame as timestamp invalid" );
          }
        } else {
          Warning( "Unable to store frame as shared memory invalid" );
        }
      } // end if buffered playback
      frame_count++;
    } else {
      Debug(4,"Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
    } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) 

    unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));
    Debug(4, "Sleeping for (%d)", sleep_time);
    usleep(sleep_time);
    if ( ttl ) {
      if ( (now.tv_sec - stream_start_time) > ttl ) {
        Debug(2, "now(%d) - start(%d) > ttl(%d) break", now.tv_sec, stream_start_time, ttl);
        break;
      }
    }
    if ( ! last_frame_sent ) {
      // If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
      last_frame_sent = now.tv_sec;
      Warning( "no last_frame_sent.  Shouldn't happen. frame_mod was (%d) frame_count (%d) ", frame_mod, frame_count );
    } else if ( (!paused) && ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) ) {
      Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame );
      break;
    }
  } // end while

  if ( buffered_playback ) {
    Debug(1, "Cleaning swap files from %s", swap_path.c_str());
    struct stat stat_buf;
    if ( stat(swap_path.c_str(), &stat_buf) < 0 ) {
      if ( errno != ENOENT ) {
        Error("Can't stat '%s': %s", swap_path.c_str(), strerror(errno));
      }
    } else if ( !S_ISDIR(stat_buf.st_mode) ) {
      Error("Swap image path '%s' is not a directory", swap_path.c_str());
    } else {
      char glob_pattern[PATH_MAX] = "";

      snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path.c_str());
      glob_t pglob;
      int glob_status = glob(glob_pattern, 0, 0, &pglob);
      if ( glob_status != 0 ) {
        if ( glob_status < 0 ) {
          Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
        } else {
          Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
        }
      } else {
        for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) {
          if ( unlink(pglob.gl_pathv[i]) < 0 ) {
            Error("Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno));
          }
        }
      }
      globfree( &pglob );
      if ( rmdir(swap_path.c_str()) < 0 ) {
        Error( "Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno) );
      }
    } // end if checking for swap_path
  } // end if buffered_playback

  closeComms();
} // end MonitorStream::runStream
예제 #16
0
Condition::~Condition()
{
  if ( pthread_cond_destroy( &mCondition ) < 0 )
    throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
}
예제 #17
0
void Condition::wait()
{
  // Locking done outside of this function
  if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
    throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
}
int RemoteCameraHttp::GetResponse()
{
  int buffer_len;
#if HAVE_LIBPCRE
  if ( method == REGEXP )
  {
    const char *header = 0;
    int header_len = 0;
    const char *http_version = 0;
    int status_code = 0;
    const char *status_mesg = 0;
    const char *connection_type = "";
    int content_length = 0;
    const char *content_type = "";
    const char *content_boundary = "";
    const char *subheader = 0;
    int subheader_len = 0;
    //int subcontent_length = 0;
    //const char *subcontent_type = "";

    while ( true )
    {
      switch( state )
      {
        case HEADER :
        {
          static RegExpr *header_expr = 0;
          static RegExpr *status_expr = 0;
          static RegExpr *connection_expr = 0;
          static RegExpr *content_length_expr = 0;
          static RegExpr *content_type_expr = 0;

          while ( ! ( buffer_len = ReadData( buffer ) ) ) {
          }
            if ( buffer_len < 0 ) {
              Error( "Unable to read header data" );
              return( -1 );
            }
          if ( !header_expr )
            header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL );
          if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 )
          {
            header = header_expr->MatchString( 1 );
            header_len = header_expr->MatchLength( 1 );
            Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header );

            if ( !status_expr )
              status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS );
            if ( status_expr->Match( header, header_len ) < 4 )
            {
              Error( "Unable to extract HTTP status from header" );
              return( -1 );
            }
            http_version = status_expr->MatchString( 1 );
            status_code = atoi( status_expr->MatchString( 2 ) );
            status_mesg = status_expr->MatchString( 3 );

            if ( status_code == 401 ) {
              if ( mNeedAuth ) {
                Error( "Failed authentication: " );
                return( -1 );
              }
              mNeedAuth = true;
              std::string Header = header;
        
              mAuthenticator->checkAuthResponse(Header);
              if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) {
                Debug( 2, "Need Digest Authentication" );
                request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version );
                request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION );
                request += stringtf( "Host: %s\r\n", host.c_str());
                if ( strcmp( config.http_version, "1.0" ) == 0 )
                  request += stringtf( "Connection: Keep-Alive\r\n" );
                request += mAuthenticator->getAuthHeader( "GET", path.c_str() );
                request += "\r\n";
                
                Debug( 2, "New request header: %s", request.c_str() );
                return( 0 );
              } 

            } else if ( status_code < 200 || status_code > 299 ) {
              Error( "Invalid response status %d: %s\n%s", status_code, status_mesg, (char *)buffer );
              return( -1 );
            }
            Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version );

            if ( !connection_expr )
              connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS );
            if ( connection_expr->Match( header, header_len ) == 2 )
            {
              connection_type = connection_expr->MatchString( 1 );
              Debug( 3, "Got connection '%s'", connection_type );
            }

            if ( !content_length_expr )
              content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS );
            if ( content_length_expr->Match( header, header_len ) == 2 )
            {
              content_length = atoi( content_length_expr->MatchString( 1 ) );
              Debug( 3, "Got content length '%d'", content_length );
            }

            if ( !content_type_expr )
              content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=\x22?(.+?)\x22?)?\r?\n", PCRE_CASELESS );
            if ( content_type_expr->Match( header, header_len ) >= 2 )
            {
              content_type = content_type_expr->MatchString( 1 );
              Debug( 3, "Got content type '%s'\n", content_type );
              if ( content_type_expr->MatchCount() > 2 )
              {
                content_boundary = content_type_expr->MatchString( 2 );
                Debug( 3, "Got content boundary '%s'", content_boundary );
              }
            }

            if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) )
            {
              // Single image
              mode = SINGLE_IMAGE;
              format = JPEG;
              state = CONTENT;
            }
            else if ( !strcasecmp( content_type, "image/x-rgb" ) )
            {
              // Single image
              mode = SINGLE_IMAGE;
              format = X_RGB;
              state = CONTENT;
            }
            else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
            {
              // Single image
              mode = SINGLE_IMAGE;
              format = X_RGBZ;
              state = CONTENT;
            }
            else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
            {
              // Image stream, so start processing
              if ( !content_boundary[0] )
              {
                Error( "No content boundary found in header '%s'", header );
                return( -1 );
              }
              mode = MULTI_IMAGE;
              state = SUBHEADER;
            }
            //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) )
            //{
              //// MPEG stream, coming soon!
            //}
            else
            {
              Error( "Unrecognised content type '%s'", content_type );
              return( -1 );
            }
            buffer.consume( header_len );
          }
          else
          {
            Debug( 3, "Unable to extract header from stream, retrying" );
            //return( -1 );
          }
          break;
        }
        case SUBHEADER :
        {
          static RegExpr *subheader_expr = 0;
          static RegExpr *subcontent_length_expr = 0;
          static RegExpr *subcontent_type_expr = 0;

          if ( !subheader_expr )
          {
            char subheader_pattern[256] = "";
            snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary );
            subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL );
          }
          if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 )
          {
            subheader = subheader_expr->MatchString( 1 );
            subheader_len = subheader_expr->MatchLength( 1 );
            Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader );

            if ( !subcontent_length_expr )
              subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS );
            if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 )
            {
              content_length = atoi( subcontent_length_expr->MatchString( 1 ) );
              Debug( 3, "Got subcontent length '%d'", content_length );
            }

            if ( !subcontent_type_expr )
              subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS );
            if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 )
            {
              content_type = subcontent_type_expr->MatchString( 1 );
              Debug( 3, "Got subcontent type '%s'", content_type );
            }

            buffer.consume( subheader_len );
            state = CONTENT;
          }
          else
          {
            Debug( 3, "Unable to extract subheader from stream, retrying" );
            while ( ! ( buffer_len = ReadData( buffer ) ) ) {
            }
              if ( buffer_len < 0 ) {
                Error( "Unable to extract subheader data" );
                return( -1 );
              }
          }
          break;
        }
        case CONTENT :
        {

          // if content_type is something like image/jpeg;size=, this will strip the ;size=
          char * semicolon = strchr( (char *)content_type, ';' );
          if ( semicolon ) {
            *semicolon = '\0';
          }

          if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) )
          {
            format = JPEG;
          }
          else if ( !strcasecmp( content_type, "image/x-rgb" ) )
          {
            format = X_RGB;
          }
          else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
          {
            format = X_RGBZ;
          }
          else
          {
            Error( "Found unsupported content type '%s'", content_type );
            return( -1 );
          }

          if ( content_length )
          {
            while ( (long)buffer.size() < content_length )
            {
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
              if ( ReadData( buffer ) < 0 ) {
                Error( "Unable to read content" );
                return( -1 );
              }
            }
            Debug( 3, "Got end of image by length, content-length = %d", content_length );
          }
          else
          {
            while ( !content_length )
            {
              while ( ! ( buffer_len = ReadData( buffer ) ) ) {
              }
                if ( buffer_len < 0 ) {
                  Error( "Unable to read content" );
                  return( -1 );
                }
              static RegExpr *content_expr = 0;
              if ( mode == MULTI_IMAGE )
              {
                if ( !content_expr )
                {
                  char content_pattern[256] = "";
                  snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary );
                  content_expr = new RegExpr( content_pattern, PCRE_DOTALL );
                }
                if ( content_expr->Match( buffer, buffer.size() ) == 2 )
                {
                  content_length = content_expr->MatchLength( 1 );
                  Debug( 3, "Got end of image by pattern, content-length = %d", content_length );
                }
              }
            }
          }
          if ( mode == SINGLE_IMAGE )
          {
            state = HEADER;
            Disconnect();
          }
          else
          {
            state = SUBHEADER;
          }
          Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() );
          return( content_length );
        }
        case HEADERCONT :
        case SUBHEADERCONT :
        {
          // Ignore
          break;
        }
      }
    }
  }
  else
#endif // HAVE_LIBPCRE
  {
    if ( method == REGEXP )
    {
      Warning( "Unable to use netcam regexps as not compiled with libpcre" );
    }
    static const char *http_match = "HTTP/";
    static const char *connection_match = "Connection:";
    static const char *content_length_match = "Content-length:";
    static const char *content_type_match = "Content-type:";
    static const char *boundary_match = "boundary=";
    static const char *authenticate_match = "WWW-Authenticate:";
    static int http_match_len = 0;
    static int connection_match_len = 0;
    static int content_length_match_len = 0;
    static int content_type_match_len = 0;
    static int boundary_match_len = 0;
    static int authenticate_match_len = 0;

    if ( !http_match_len )
      http_match_len = strlen( http_match );
    if ( !connection_match_len )
      connection_match_len = strlen( connection_match );
    if ( !content_length_match_len )
      content_length_match_len = strlen( content_length_match );
    if ( !content_type_match_len )
      content_type_match_len = strlen( content_type_match );
    if ( !boundary_match_len )
      boundary_match_len = strlen( boundary_match );
    if ( !authenticate_match_len )
      authenticate_match_len = strlen( authenticate_match );

    static int n_headers;
    //static char *headers[32];

    static int n_subheaders;
    //static char *subheaders[32];

    static char *http_header;
    static char *connection_header;
    static char *content_length_header;
    static char *content_type_header;
    static char *boundary_header;
    static char *authenticate_header;
    static char subcontent_length_header[32];
    static char subcontent_type_header[64];
  
    static char http_version[16];
    static char status_code[16];
    static char status_mesg[256];
    static char connection_type[32];
    static int content_length;
    static char content_type[32];
    static char content_boundary[64];
    static int content_boundary_len;

    while ( true )
    {
      switch( state )
      {
        case HEADER :
        {
          n_headers = 0;
          http_header = 0;
          connection_header = 0;
          content_length_header = 0;
          content_type_header = 0;
          authenticate_header = 0;

          http_version[0] = '\0';
          status_code [0]= '\0';
          status_mesg [0]= '\0';
          connection_type [0]= '\0';
          content_length = 0;
          content_type[0] = '\0';
          content_boundary[0] = '\0';
          content_boundary_len = 0;
        }
        case HEADERCONT :
        {
          while ( ! ( buffer_len = ReadData( buffer ) ) ) {
          }
            if ( buffer_len < 0 ) {
              Error( "Unable to read header" );
              return( -1 );
            }

          char *crlf = 0;
          char *header_ptr = (char *)buffer;
          int header_len = buffer.size();
          bool all_headers = false;

          while( true )
          {
            int crlf_len = memspn( header_ptr, "\r\n", header_len );
            if ( n_headers )
            {
              if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) )
              {
                *header_ptr = '\0';
                header_ptr += crlf_len;
                header_len -= buffer.consume( header_ptr-(char *)buffer );
                all_headers = true;
                break;
              }
            }
            if ( crlf_len )
            {
              if ( header_len == crlf_len )
              {
                break;
              }
              else
              {
                *header_ptr = '\0';
                header_ptr += crlf_len;
                header_len -= buffer.consume( header_ptr-(char *)buffer );
              }
            }

            Debug( 6, "%s", header_ptr );
            if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) )
            {
              //headers[n_headers++] = header_ptr;
              n_headers++;

              if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) )
              {
                http_header = header_ptr+http_match_len;
                Debug( 6, "Got http header '%s'", header_ptr );
              }
              else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) )
              {
                connection_header = header_ptr+connection_match_len;
                Debug( 6, "Got connection header '%s'", header_ptr );
              }
              else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) )
              {
                content_length_header = header_ptr+content_length_match_len;
                Debug( 6, "Got content length header '%s'", header_ptr );
              }

              else if ( !authenticate_header && (strncasecmp( header_ptr, authenticate_match, authenticate_match_len) == 0) )
              {
                authenticate_header = header_ptr;
                Debug( 6, "Got authenticate header '%s'", header_ptr );
              }
              else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) )
              {
                content_type_header = header_ptr+content_type_match_len;
                Debug( 6, "Got content type header '%s'", header_ptr );
              }
              else
              {
                Debug( 6, "Got ignored header '%s'", header_ptr );
              }
              header_ptr = crlf;
              header_len -= buffer.consume( header_ptr-(char *)buffer );
            }
            else
            {
              // No end of line found
              break;
            }
          }

          if ( all_headers )
          {
            char *start_ptr, *end_ptr;

            if ( !http_header )
            {
              Error( "Unable to extract HTTP status from header" );
              return( -1 );
            }

            start_ptr = http_header;
            end_ptr = start_ptr+strspn( start_ptr, "10." );

            memset( http_version, 0, sizeof(http_version) );
            strncpy( http_version, start_ptr, end_ptr-start_ptr );

            start_ptr = end_ptr;
            start_ptr += strspn( start_ptr, " " );
            end_ptr = start_ptr+strspn( start_ptr, "0123456789" );

            memset( status_code, 0, sizeof(status_code) );
            strncpy( status_code, start_ptr, end_ptr-start_ptr );
            int status = atoi( status_code );

            start_ptr = end_ptr;
            start_ptr += strspn( start_ptr, " " );
            strcpy( status_mesg, start_ptr );

            if ( status == 401 ) {
              if ( mNeedAuth ) {
                Error( "Failed authentication: " );
                return( -1 );
              }
              if ( ! authenticate_header ) {
                Error( "Failed authentication, but don't have an authentication header: " );
                return( -1 );
              }
              mNeedAuth = true;
              std::string Header = authenticate_header;
              Debug(2, "Checking for digest auth in %s", authenticate_header );

              mAuthenticator->checkAuthResponse(Header);
              if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) {
                Debug( 2, "Need Digest Authentication" );
                request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version );
                request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION );
                request += stringtf( "Host: %s\r\n", host.c_str());
                if ( strcmp( config.http_version, "1.0" ) == 0 )
                  request += stringtf( "Connection: Keep-Alive\r\n" );
                request += mAuthenticator->getAuthHeader( "GET", path.c_str() );
                request += "\r\n";

                Debug( 2, "New request header: %s", request.c_str() );
                return( 0 );
              } else {
                Debug( 2, "Need some other kind of Authentication" );
              }
            } else if ( status < 200 || status > 299 )
            {
              Error( "Invalid response status %s: %s", status_code, status_mesg );
              return( -1 );
            }
            Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version );

            if ( connection_header )
            {
              memset( connection_type, 0, sizeof(connection_type) );
              start_ptr = connection_header + strspn( connection_header, " " );
              strcpy( connection_type, start_ptr );
              Debug( 3, "Got connection '%s'", connection_type );
            }
            if ( content_length_header )
            {
              start_ptr = content_length_header + strspn( content_length_header, " " );
              content_length = atoi( start_ptr );
              Debug( 3, "Got content length '%d'", content_length );
            }
            if ( content_type_header )
            {
              memset( content_type, 0, sizeof(content_type) );
              start_ptr = content_type_header + strspn( content_type_header, " " );
              if ( (end_ptr = strchr( start_ptr, ';' )) )
              {
                strncpy( content_type, start_ptr, end_ptr-start_ptr );
                Debug( 3, "Got content type '%s'", content_type );

                start_ptr = end_ptr + strspn( end_ptr, "; " );

                if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 )
                {
                  start_ptr += boundary_match_len;
                  start_ptr += strspn( start_ptr, "-" );
                  content_boundary_len = sprintf( content_boundary, "--%s", start_ptr );
                  Debug( 3, "Got content boundary '%s'", content_boundary );
                }
                else
                {
                  Error( "No content boundary found in header '%s'", content_type_header );
                }
              }
              else
              {
                strcpy( content_type, start_ptr );
                Debug( 3, "Got content type '%s'", content_type );
              }
            }

            if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) )
            {
              // Single image
              mode = SINGLE_IMAGE;
              format = JPEG;
              state = CONTENT;
            }
            else if ( !strcasecmp( content_type, "image/x-rgb" ) )
            {
              // Single image
              mode = SINGLE_IMAGE;
              format = X_RGB;
              state = CONTENT;
            }
            else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
            {
              // Single image
              mode = SINGLE_IMAGE;
              format = X_RGBZ;
              state = CONTENT;
            }
            else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
            {
              // Image stream, so start processing
              if ( !content_boundary[0] )
              {
                Error( "No content boundary found in header '%s'", content_type_header );
                return( -1 );
              }
              mode = MULTI_IMAGE;
              state = SUBHEADER;
            }
            //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) )
            //{
              //// MPEG stream, coming soon!
            //}
            else
            {
              Error( "Unrecognised content type '%s'", content_type );
              return( -1 );
            }
          }
          else
          {
            Debug( 3, "Unable to extract entire header from stream, continuing" );
            state = HEADERCONT;
            //return( -1 );
          }
          break;
        }
        case SUBHEADER :
        {
          n_subheaders = 0;
          boundary_header = 0;
          subcontent_length_header[0] = '\0';
          subcontent_type_header[0] = '\0';
          content_length = 0;
          content_type[0] = '\0';
        }
        case SUBHEADERCONT :
        {
          char *crlf = 0;
          char *subheader_ptr = (char *)buffer;
          int subheader_len = buffer.size();
          bool all_headers = false;

          while( true )
          {
            int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len );
            if ( n_subheaders )
            {
              if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) )
              {
                *subheader_ptr = '\0';
                subheader_ptr += crlf_len;
                subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
                all_headers = true;
                break;
              }
            }
            if ( crlf_len )
            {
              if ( subheader_len == crlf_len )
              {
                break;
              }
              else
              {
                *subheader_ptr = '\0';
                subheader_ptr += crlf_len;
                subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
              }
            }

            Debug( 6, "%d: %s", subheader_len, subheader_ptr );

            if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) )
            {
              //subheaders[n_subheaders++] = subheader_ptr;
              n_subheaders++;

              if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) )
              {
                boundary_header = subheader_ptr;
                Debug( 4, "Got boundary subheader '%s'", subheader_ptr );
              }
              else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) )
              {
                strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) );
                *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0';
                Debug( 4, "Got content length subheader '%s'", subcontent_length_header );
              }
              else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) )
              {
                strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) );
                *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0';
                Debug( 4, "Got content type subheader '%s'", subcontent_type_header );
              }
              else
              {
                Debug( 6, "Got ignored subheader '%s' found", subheader_ptr );
              }
              subheader_ptr = crlf;
              subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
            }
            else
            {
              // No line end found
              break;
            }
          }
          
          if ( all_headers && boundary_header )
          {
            char *start_ptr/*, *end_ptr*/;

            Debug( 3, "Got boundary '%s'", boundary_header );

            if ( subcontent_length_header[0] )
            {
              start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " );
              content_length = atoi( start_ptr );
              Debug( 3, "Got subcontent length '%d'", content_length );
            }
            if ( subcontent_type_header[0] )
            {
              memset( content_type, 0, sizeof(content_type) );
              start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " );
              strcpy( content_type, start_ptr );
              Debug( 3, "Got subcontent type '%s'", content_type );
            }
            state = CONTENT;
          }
          else
          {
            Debug( 3, "Unable to extract subheader from stream, retrying" );
            while ( ! ( buffer_len = ReadData( buffer ) ) ) {
            }
              if ( buffer_len < 0 ) {
                Error( "Unable to read subheader" );
                return( -1 );
              }
            state = SUBHEADERCONT;
          }
          break;
        }
        case CONTENT :
        {

          // if content_type is something like image/jpeg;size=, this will strip the ;size=
          char * semicolon = strchr( content_type, ';' );
          if ( semicolon ) {
            *semicolon = '\0';
          }

          if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) )
          {
            format = JPEG;
          }
          else if ( !strcasecmp( content_type, "image/x-rgb" ) )
          {
            format = X_RGB;
          }
          else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
          {
            format = X_RGBZ;
          }
          else
          {
            Error( "Found unsupported content type '%s'", content_type );
            return( -1 );
          }

          if ( format == JPEG && buffer.size() >= 2 )
          {
            if ( buffer[0] != 0xff || buffer[1] != 0xd8 )
            {
              Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] );
              return( -1 );
            }
          }

          if ( content_length )
          {
            while ( (long)buffer.size() < content_length )
            {
              //int buffer_len = ReadData( buffer, content_length-buffer.size() );
              if ( ReadData( buffer ) < 0 ) {
                Error( "Unable to read content" );
                return( -1 );
              }
            }
            Debug( 3, "Got end of image by length, content-length = %d", content_length );
          }
          else
          {
            int content_pos = 0;
            while ( !content_length )
            {
              buffer_len = ReadData( buffer );
              if ( buffer_len < 0 )
              {
                Error( "Unable to read content" );
                return( -1 );
              }
              int buffer_size = buffer.size();
              if ( buffer_len )
              {
                if ( mode == MULTI_IMAGE )
                {
                  while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) )
                  {
                    content_length = start_ptr - (char *)buffer;
                    Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length );
                    break;
                  }
                }
              }
              else
              {
                content_length = buffer_size;
                Debug( 3, "Got end of image by closure, content-length = %d", content_length );
                if ( mode == SINGLE_IMAGE )
                {
                  char *end_ptr = (char *)buffer+buffer_size;

                  while( *end_ptr == '\r' || *end_ptr == '\n' )
                  {
                    content_length--;
                    end_ptr--;
                  }

                  if ( end_ptr != ((char *)buffer+buffer_size) )
                  {
                    Debug( 3, "Trimmed end of image, new content-length = %d", content_length );
                  }
                }
              }
            }
          }
          if ( mode == SINGLE_IMAGE )
          {
            state = HEADER;
            Disconnect();
          }
          else
          {
            state = SUBHEADER;
          }

          if ( format == JPEG && buffer.size() >= 2 )
          {
            if ( buffer[0] != 0xff || buffer[1] != 0xd8 )
            {
              Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] );
              return( -1 );
            }
          }

          Debug( 3, "Returning %d bytes, buffer size: (%d) bytes of captured content", content_length, buffer.size() );
          return( content_length );
        }
      }
    }
  }
  return( 0 );
}
예제 #19
0
void Condition::signal()
{
  if ( pthread_cond_signal( &mCondition ) < 0 )
    throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
}
예제 #20
0
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) : 
    mUrl( url ),
    mConnInfo( 0 ),
    mBandInfo( 0 )
{
    MediaDescriptor *currMedia = 0;

    StringVector lines = split( sdp, "\r\n" );
    for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
    {
        std::string line = *iter;
        if ( line.empty() )
            break;

        Debug( 3, "Processing SDP line '%s'", line.c_str() );
        const char sdpType = line[0];
        if ( line[1] != '=' )
            throw Exception( "Invalid SDP format at '"+line+"'" );

        line.erase( 0, 2 );
        switch( sdpType )
        {
            case 'v' :
                mVersion = line;
                break;
            case 'o' :
                mOwner = line;
                break;
            case 's' :
                mName = line;
                break;
            case 'i' :
                mInfo = line;
                break;
            case 'c' :
                mConnInfo = new ConnInfo( line );
                break;
            case 'b' :
                mBandInfo = new BandInfo( line );
                break;
            case 't' :
                mTimeInfo = line;
                break;
            case 'a' :
            {
                mAttributes.push_back( line );
                StringVector tokens = split( line, ":", 2 );
                std::string attrName = tokens[0];
                if ( currMedia )
                {
                    if ( attrName == "control" )
                    {
                        if ( tokens.size() < 2 )
                            throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
                        currMedia->setControlUrl( tokens[1] );
                    }
                    else if ( attrName == "range" )
                    {
                    }
                    else if ( attrName == "rtpmap" )
                    {
                        // a=rtpmap:96 MP4V-ES/90000
                        if ( tokens.size() < 2 )
                            throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
                        StringVector attrTokens = split( tokens[1], " " );
                        int payloadType = atoi(attrTokens[0].c_str());
                        if ( payloadType != currMedia->getPayloadType() )
                            throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
                        std::string payloadDesc = attrTokens[1];
                        //currMedia->setPayloadType( payloadType );
                        if ( attrTokens.size() > 1 )
                        {
                            StringVector payloadTokens = split( attrTokens[1], "/" );
                            std::string payloadDesc = payloadTokens[0];
                            int payloadClock = atoi(payloadTokens[1].c_str());
                            currMedia->setPayloadDesc( payloadDesc );
                            currMedia->setClock( payloadClock );
                        }
                    }
                    else if ( attrName == "framesize" )
                    {
                        // a=framesize:96 320-240
                        if ( tokens.size() < 2 )
                            throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
                        StringVector attrTokens = split( tokens[1], " " );
                        int payloadType = atoi(attrTokens[0].c_str());
                        if ( payloadType != currMedia->getPayloadType() )
                            throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
                        //currMedia->setPayloadType( payloadType );
                        StringVector sizeTokens = split( attrTokens[1], "-" );
                        int width = atoi(sizeTokens[0].c_str());
                        int height = atoi(sizeTokens[1].c_str());
                        currMedia->setFrameSize( width, height );
                    }
                    else if ( attrName == "framerate" )
                    {
                        // a=framerate:5.0
                        if ( tokens.size() < 2 )
                            throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
                        double frameRate = atof(tokens[1].c_str());
                        currMedia->setFrameRate( frameRate );
                    }
                    else if ( attrName == "fmtp" )
                    {
                        // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
                        if ( tokens.size() < 2 )
                            throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
                        StringVector attrTokens = split( tokens[1], " ", 2 );
                        int payloadType = atoi(attrTokens[0].c_str());
                        if ( payloadType != currMedia->getPayloadType() )
                            throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
                        //currMedia->setPayloadType( payloadType );
                        if ( attrTokens.size() > 1 )
                        {
                            StringVector attr2Tokens = split( attrTokens[1], "; " );
                            for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
                            {
                                StringVector attr3Tokens = split( attr2Tokens[i], "=" );
                                //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
                                if ( attr3Tokens[0] == "profile-level-id" )
                                {
                                }
                                else if ( attr3Tokens[0] == "config" )
                                {
                                }
                                else 
                                {
                                    Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
                                }
                            }
                        }
                    }
                    else if ( attrName == "mpeg4-iod" )
                    {
                        // a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
                    }
                    else if ( attrName == "mpeg4-esid" )
                    {
                        // a=mpeg4-esid:201
                    }
                    else
                    {
                        Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
                    }
                }
                else
                {
                    Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
                }
                break;
            }
예제 #21
0
void Condition::broadcast()
{
  if ( pthread_cond_broadcast( &mCondition ) < 0 )
    throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
}
예제 #22
0
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
    // The "response" field is computed as:
    //    md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
    size_t md5len = 16;
    unsigned char md5buf[md5len];
    char md5HexBuf[md5len*2+1];
    
    // Step 1: md5(<username>:<realm>:<password>)
    std::string ha1Data = username() + ":" + realm() + ":" + password();
	Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
#if HAVE_DECL_MD5
    MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
#elif HAVE_DECL_GNUTLS_FINGERPRINT
    gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
    gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
#endif
    for ( unsigned int j = 0; j < md5len; j++ )
    {
        sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
    }
    md5HexBuf[md5len*2]='\0';
    std::string ha1Hash = md5HexBuf;
    
    // Step 2: md5(<cmd>:<url>)
    std::string ha2Data = method + ":" + uri;
	Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
#if HAVE_DECL_MD5
    MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
#elif HAVE_DECL_GNUTLS_FINGERPRINT
    gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
    gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
#endif
    for ( unsigned int j = 0; j < md5len; j++ )
    {
        sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
    }
    md5HexBuf[md5len*2]='\0';
    std::string ha2Hash = md5HexBuf;

    // Step 3: md5(ha1:<nonce>:ha2)
    std::string digestData = ha1Hash + ":" + nonce();
	if ( ! fQop.empty() ) {
		digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
		nc ++;
		// if qop was specified, then we have to include t and a cnonce and an nccount
	}
	digestData += ":" + ha2Hash;
	Debug( 2, "pre-md5: %s", digestData.c_str() );
#if HAVE_DECL_MD5
    MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
#elif HAVE_DECL_GNUTLS_FINGERPRINT
    gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
    gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
#endif
    for ( unsigned int j = 0; j < md5len; j++ )
    {
        sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
    }
    md5HexBuf[md5len*2]='\0';
   
    return md5HexBuf;
#else // HAVE_DECL_MD5
	Error( "You need to build with gnutls or openssl installed to use digest authentication" );
    return( 0 );
#endif // HAVE_DECL_MD5
}
예제 #23
0
/**
* @brief 
*
* @param requestType
* @param requestUrl
* @param requestHeaders
* @param responseHeaders
*
* @return 
*/
bool RtspSession::recvRequest( const std::string &requestType, const std::string &requestUrl, const Connection::Headers &requestHeaders, Connection::Headers &responseHeaders )
{
    Debug( 2, "Session %d - Processing RTSP request: %s", mSession, requestType.c_str() );

    if ( requestType == "SETUP" )
    {
        std::string requestHost, requestStreamName, requestStreamSource;
        int trackId = -1;
        FeedProvider *provider = mConnection->validateRequestUrl( requestUrl, &requestHost, &requestStreamName, &requestStreamSource, &trackId );
        if ( !provider )
        {
            mConnection->sendResponse( responseHeaders, "", 404, "Not Found" );
            return( false );
        }
        if ( !provider->ready() )
        {
            mConnection->sendResponse( responseHeaders, "", 503, "Stream not ready" );
            return( false );
        }
 
        if ( trackId < 0 || trackId > 1 )
        {
            Error( "Got invalid trackID %d from request URL '%s'", trackId, requestUrl.c_str() );
            return( false );
        }

        if ( mRtspStreams.find( trackId ) != mRtspStreams.end() )
        {
            Error( "Got RTSP SETUP for existing trackID %d", trackId );
            return( false );
        }

        Connection::Headers::const_iterator iter = requestHeaders.find("Transport");
        if ( iter == requestHeaders.end() )
        {
            Error( "No Transport header found in SETUP" );
            return( false );
        }
        std::string transportSpec = iter->second;

        RtspStream *stream = newStream( trackId, mEncoder, transportSpec );

        transportSpec.clear();
        transportSpec += "RTP/AVP";
        if ( stream->lowerTransport() == RtspStream::LOWTRANS_TCP )
        {
            transportSpec += "/TCP";
        }
        else
        {
            transportSpec += "/UDP";
            if ( stream->delivery() == RtspStream::DEL_UNICAST )
                transportSpec += ";unicast";
            else
                transportSpec += ";multicast";
        }
        if ( stream->interleaved() )
        {
            transportSpec += stringtf( ";interleaved=%d-%d", stream->channel(0), stream->channel(1) );
        }
        else
        {
            transportSpec += stringtf( ";client_port=%d-%d", stream->destPort(0), stream->destPort(1) );
            transportSpec += stringtf( ";server_port=%d-%d", stream->sourcePort(0), stream->sourcePort(1) );
        }
        transportSpec += stringtf( ";ssrc=%08X", stream->ssrc() );
        transportSpec += ";mode=\"PLAY\"";
        responseHeaders.insert( Connection::Headers::value_type( "Session", stringtf( "%08X", mSession ) ) );
        responseHeaders.insert( Connection::Headers::value_type( "Transport", transportSpec ) );

        state( READY );
        return( true );
    }
    else if ( requestType == "PLAY" )
    {
        std::string rtpInfo;
        for ( RtspStreams::iterator iter = mRtspStreams.begin(); iter != mRtspStreams.end(); iter++ )
        {
            const RtspStream *stream = iter->second;
            if ( rtpInfo.size() )
                rtpInfo += ",";
            rtpInfo += stringtf( "url=%s/trackID=%d;seq=0;rtptime=0", requestUrl.c_str(), stream->trackId() );
        }
        responseHeaders.insert( Connection::Headers::value_type( "Session", stringtf( "%08X", mSession ) ) );
        responseHeaders.insert( Connection::Headers::value_type( "Range", "npt=0-" ) );
        responseHeaders.insert( Connection::Headers::value_type( "RTP-Info", rtpInfo ) );
        for ( RtspStreams::iterator iter = mRtspStreams.begin(); iter != mRtspStreams.end(); iter++ )
            iter->second->start();
        state( PLAYING );
        return( true );
    }
    else if ( requestType == "GET_PARAMETER" )
    {
        FeedProvider *provider = mConnection->validateRequestUrl( requestUrl );
        if ( provider )
        {
            responseHeaders.insert( Connection::Headers::value_type( "Session", stringtf( "%08X", mSession ) ) );
            return( true );
        }
        mConnection->sendResponse( responseHeaders, "", 451, "Parameter Not Understood" );
        return( false );
    }
    else if ( requestType == "TEARDOWN" )
    {
        Debug( 1, "Stopping streams" );
        for ( RtspStreams::iterator iter = mRtspStreams.begin(); iter != mRtspStreams.end(); iter++ )
            iter->second->stop();
        Debug( 1, "Waiting for streams" );
        for ( RtspStreams::iterator iter = mRtspStreams.begin(); iter != mRtspStreams.end(); iter++ )
            iter->second->join();
        for ( RtspStreams::iterator iter = mRtspStreams.begin(); iter != mRtspStreams.end(); iter++ )
            delete iter->second;
        mRtspStreams.clear();
        state( INIT );
        return( true );
    }
    Error( "Unrecognised RTSP command '%s'", requestType.c_str() );
    return( false );
}