Exemple #1
0
bool RtspThread::recvResponse( std::string &response )
{
    if ( mRtspSocket.recv( response ) < 0 )
        Error( "Recv failed; %s", strerror(errno) );
    Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() );
    float respVer = 0;
    int respCode = -1;
    char respText[ZM_NETWORK_BUFSIZ];
    if ( sscanf( response.c_str(), "RTSP/%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( false );
    }
    if ( respCode != 200 )
    {
        Error( "Unexpected response code %d, text is '%s'", respCode, respText );
        return( false );
    }
    return( true );
}
/**
* @brief 
*
* @param buffer
*
* @return 
*/
bool RtspConnection::handleRequest( const ByteBuffer &buffer )
{
    int channel= buffer[1];
    uint16_t length = buffer.size()-4;
    Debug( 2, "Got %d byte packet on channel %d", length, channel );
    Hexdump( 2, buffer+4, length );
    return( true );
}
Exemple #3
0
int main()
{
    unsigned char data[150];
    unsigned char c = 0;
    for (auto& val : data)
    {
        val = c++;
    }

    std::cout << Hexdump(data, sizeof(data)) << std::endl;
    std::cout << CustomHexdump<8, true>(data, sizeof(data)) << std::endl;
    std::cout << CustomHexdump<32, false>(data, sizeof(data)) << std::endl;

}
Exemple #4
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 );
}
/**
* @brief 
*
* @return 
*/
int H264Encoder::run()
{
    // TODO - This section needs to be rewritten to read the configuration from the values saved
    // for the streams via the web gui
    AVDictionary *opts = NULL;
    //avSetH264Preset( &opts, "default" );
    //avSetH264Profile( &opts, "main" );
    //avDictSet( &opts, "level", "4.1" );
    avSetH264Preset( &opts, "ultrafast" );
    //avSetH264Profile( &opts, "baseline" );
    avDictSet( &opts, "level", "31" );
    avDictSet( &opts, "g", "24" );
    //avDictSet( &opts, "b", (int)mBitRate );
    //avDictSet( &opts, "bitrate", (int)mBitRate );
    //avDictSet( &opts, "crf", "24" );
    //avDictSet( &opts, "framerate", (double)mFrameRate );
    //avDictSet( &opts, "fps", (double)mFrameRate );
    //avDictSet( &opts, "r", (double)mFrameRate );
    //avDictSet( &opts, "timebase", "1/90000" );
    avDumpDict( opts );

    // Make sure ffmpeg is compiled with libx264 support
    AVCodec *codec = avcodec_find_encoder( CODEC_ID_H264 );
    if ( !codec )
        Fatal( "Can't find encoder codec" );

    mCodecContext = avcodec_alloc_context3( codec );

    mCodecContext->width = mWidth;
    mCodecContext->height = mHeight;
    //mCodecContext->time_base = TimeBase( 1, 90000 );
    mCodecContext->time_base = mFrameRate.timeBase();
    mCodecContext->bit_rate = mBitRate;
    mCodecContext->pix_fmt = mPixelFormat;

    mCodecContext->gop_size = 24;
    //mCodecContext->max_b_frames = 1;

    Debug( 2, "Time base = %d/%d", mCodecContext->time_base.num, mCodecContext->time_base.den );
    Debug( 2, "Pix fmt = %d", mCodecContext->pix_fmt );

    /* open it */
    if ( avcodec_open2( mCodecContext, codec, &opts ) < 0 )
        Fatal( "Unable to open encoder codec" );

    avDumpDict( opts );
    AVFrame *inputFrame = avcodec_alloc_frame();

    Info( "%s:Waiting", cidentity() );
    if ( waitForProviders() )
    {
        Info( "%s:Waited", cidentity() );

        // Find the source codec context
        uint16_t inputWidth = videoProvider()->width();
        uint16_t inputHeight = videoProvider()->height();
        PixelFormat inputPixelFormat = videoProvider()->pixelFormat();
        //FrameRate inputFrameRate = videoProvider()->frameRate();
        //Info( "CONVERT: %d-%dx%d -> %d-%dx%d",
            //inputPixelFormat, inputWidth, inputHeight,
            //mPixelFormat, mWidth, mHeight
        //);

        // Make space for anything that is going to be output
        AVFrame *outputFrame = avcodec_alloc_frame();
        ByteBuffer outputBuffer;
        outputBuffer.size( avpicture_get_size( mCodecContext->pix_fmt, mCodecContext->width, mCodecContext->height ) );
        avpicture_fill( (AVPicture *)outputFrame, outputBuffer.data(), mCodecContext->pix_fmt, mCodecContext->width, mCodecContext->height );

        // Prepare for image format and size conversions
        struct SwsContext *convertContext = sws_getContext( inputWidth, inputHeight, inputPixelFormat, mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL );
        if ( !convertContext )
            Fatal( "Unable to create conversion context for encoder" );

        int outSize = 0;
        uint64_t timeInterval = mFrameRate.intervalUsec();
        uint64_t currTime = time64();
        uint64_t nextTime = currTime;
        //outputFrame->pts = currTime;
        outputFrame->pts = 0;
        uint32_t ptsInterval = 90000/mFrameRate.toInt();
        //uint32_t ptsInterval = mFrameRate.intervalPTS( mCodecContext->time_base );
        while ( !mStop )
        {
            // Synchronise the output with the desired output frame rate
            while ( currTime < nextTime )
            {
                currTime = time64();
                usleep( 1000 );
            }
            nextTime += timeInterval;

            FramePtr framePtr;
            mQueueMutex.lock();
            if ( !mFrameQueue.empty() )
            {
                if ( mInitialFrame.empty() || !mConsumers.empty() )
                {
                    FrameQueue::iterator iter = mFrameQueue.begin();
                    framePtr = *iter;
                }
                mFrameQueue.clear();
            }
            mQueueMutex.unlock();

            if ( framePtr.get() )
            {
                const FeedFrame *frame = framePtr.get();
                const VideoFrame *inputVideoFrame = dynamic_cast<const VideoFrame *>(frame);

                //Info( "Provider: %s, Source: %s, Frame: %d", inputVideoFrame->provider()->cidentity(), inputVideoFrame->originator()->cidentity(), inputVideoFrame->id() );
                //Info( "PF:%d @ %dx%d", inputVideoFrame->pixelFormat(), inputVideoFrame->width(), inputVideoFrame->height() );

                avpicture_fill( (AVPicture *)inputFrame, inputVideoFrame->buffer().data(), inputPixelFormat, inputWidth, inputHeight );

                //outputFrame->pts = currTime;
                //Debug( 5, "PTS %jd", outputFrame->pts );
       
                // Reformat the input frame to fit the desired output format
                //Info( "SCALE: %d -> %d", int(inputFrame->data[0])%16, int(outputFrame->data[0])%16 );
                if ( sws_scale( convertContext, inputFrame->data, inputFrame->linesize, 0, inputHeight, outputFrame->data, outputFrame->linesize ) < 0 )
                    Fatal( "Unable to convert input frame (%d@%dx%d) to output frame (%d@%dx%d) at frame %ju", inputPixelFormat, inputWidth, inputHeight, mCodecContext->pix_fmt, mCodecContext->width, mCodecContext->height, mFrameCount );

                // Encode the image
                outSize = avcodec_encode_video( mCodecContext, outputBuffer.data(), outputBuffer.capacity(), outputFrame );
                Debug( 5, "Encoding reports %d bytes", outSize );
                if ( outSize > 0 )
                {
                    //Info( "CPTS: %jd", mCodecContext->coded_frame->pts );
                    outputBuffer.size( outSize );
                    //Debug( 5, "PTS2 %jd", mCodecContext->coded_frame->pts );
                    if ( mInitialFrame.empty() )
                    {
                        Debug( 3, "Looking for H.264 stream info" );
                        const uint8_t *startPos = outputBuffer.head();
                        startPos = h264StartCode( startPos, outputBuffer.tail() );
                        while ( startPos < outputBuffer.tail() )
                        {
                            while( !*(startPos++) )
                                ;
                            const uint8_t *nextStartPos = h264StartCode( startPos, outputBuffer.tail() );

                            int frameSize = nextStartPos-startPos;

                            unsigned char type = startPos[0] & 0x1F;
                            unsigned char nri = startPos[0] & 0x60;
                            Debug( 1, "Type %d, NRI %d (%02x)", type, nri>>5, startPos[0] );

                            if ( type == NAL_SEI )
                            {
                                // SEI
                                mSei.assign( startPos, frameSize );
                            }
                            else if ( type == NAL_SPS )
                            {
                                // SPS
                                Hexdump( 2, startPos, frameSize );
                                mSps.assign( startPos, frameSize );

                                if ( frameSize < 4 )
                                    Panic( "H.264 NAL type 7 frame too short (%d bytes) to extract level/profile", frameSize );
                                mAvcLevel = startPos[3];
                                mAvcProfile = startPos[1];
                                Debug( 2, "Got AVC level %d, profile %d", mAvcLevel, mAvcProfile );
                            }
                            else if ( type == NAL_PPS )
                            {
                                // PPS
                                Hexdump( 2, startPos, frameSize );
                                mPps.assign( startPos, frameSize );
                            }
                            startPos = nextStartPos;
                        }
                        mInitialFrame = outputBuffer;
                        //VideoFrame *outputVideoFrame = new VideoFrame( this, ++mFrameCount, mCodecContext->coded_frame->pts, mInitialFrame );
                    }
                    else
                    {
                        //av_rescale_q(cocontext->coded_frame->pts, cocontext->time_base, videostm->time_base); 
                        VideoFrame *outputVideoFrame = new VideoFrame( this, ++mFrameCount, mCodecContext->coded_frame->pts, outputBuffer );
                        distributeFrame( FramePtr( outputVideoFrame ) );
                    }
                }
                outputFrame->pts += ptsInterval;   ///< FIXME - This can't be right, but it works...
            }
Exemple #6
0
VOID
FileEvtIoDeviceControl(
    IN WDFQUEUE         Queue,
    IN WDFREQUEST       Request,
    IN size_t            OutputBufferLength,
    IN size_t            InputBufferLength,
    IN ULONG            IoControlCode
    )
/*++
Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.
    Request - Handle to a framework request object.

    OutputBufferLength - length of the request's output buffer,
                        if an output buffer is available.
    InputBufferLength - length of the request's input buffer,
                        if an input buffer is available.

    IoControlCode - the driver-defined or system-defined I/O control code
                    (IOCTL) that is associated with the request.

Return Value:

   VOID

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;// Assume success
    PCHAR               inBuf = NULL, outBuf = NULL; // pointer to Input and output buffer
    PCHAR               data = "this String is from Device Driver !!!";
    ULONG               datalen = (ULONG) strlen(data)+1;//Length of data including null
    PCHAR               buffer = NULL;
    PREQUEST_CONTEXT    reqContext = NULL;
    size_t               bufSize;

    UNREFERENCED_PARAMETER( Queue );

    PAGED_CODE();

    if(!OutputBufferLength || !InputBufferLength)
    {
        WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
        return;
    }

    //
    // Determine which I/O control code was specified.
    //

    switch (IoControlCode)
    {
    case IOCTL_NONPNP_METHOD_BUFFERED:


        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_BUFFERED\n");

        //
        // For bufffered ioctls WdfRequestRetrieveInputBuffer &
        // WdfRequestRetrieveOutputBuffer return the same buffer
        // pointer (Irp->AssociatedIrp.SystemBuffer), so read the
        // content of the buffer before writing to it.
        //
        status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize);
        if(!NT_SUCCESS(status)) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        ASSERT(bufSize == InputBufferLength);

        //
        // Read the input buffer content.
        // We are using the following function to print characters instead
        // TraceEvents with %s format because the string we get may or
        // may not be null terminated. The buffer may contain non-printable
        // characters also.
        //
        Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data from User : %!HEXDUMP!\n",
                        log_xstr(inBuf, (USHORT)InputBufferLength)));
        PrintChars(inBuf, InputBufferLength  );


        status = WdfRequestRetrieveOutputBuffer(Request, 0, &outBuf, &bufSize);
        if(!NT_SUCCESS(status)) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        ASSERT(bufSize == OutputBufferLength);

        //
        // Writing to the buffer over-writes the input buffer content
        //

        RtlCopyMemory(outBuf, data, OutputBufferLength);

        Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data to User : %!HEXDUMP!\n",
                        log_xstr(outBuf, (USHORT)datalen)));
        PrintChars(outBuf, datalen  );

        //
        // Assign the length of the data copied to IoStatus.Information
        // of the request and complete the request.
        //
        WdfRequestSetInformation(Request,
                OutputBufferLength < datalen? OutputBufferLength:datalen);

        //
        // When the request is completed the content of the SystemBuffer
        // is copied to the User output buffer and the SystemBuffer is
        // is freed.
        //

       break;


    case IOCTL_NONPNP_METHOD_IN_DIRECT:


        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_IN_DIRECT\n");

        //
        // Get the Input buffer. WdfRequestRetrieveInputBuffer returns
        // Irp->AssociatedIrp.SystemBuffer.
        //
        status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize);
        if(!NT_SUCCESS(status)) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        ASSERT(bufSize == InputBufferLength);

        Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data from User : %!HEXDUMP!\n",
                        log_xstr(inBuf, (USHORT)InputBufferLength)));
        PrintChars(inBuf, InputBufferLength);

        //
        // Get the output buffer. Framework calls MmGetSystemAddressForMdlSafe
        // on the Irp->MdlAddress and returns the system address.
        // Oddity: For this method, this buffer is intended for transfering data
        // from the application to the driver.
        //

        status = WdfRequestRetrieveOutputBuffer(Request, 0, &buffer, &bufSize);
        if(!NT_SUCCESS(status)) {
            break;
        }

        ASSERT(bufSize == OutputBufferLength);

        Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data from User in OutputBuffer: %!HEXDUMP!\n",
                        log_xstr(buffer, (USHORT)OutputBufferLength)));
        PrintChars(buffer, OutputBufferLength);

        //
        // Return total bytes read from the output buffer.
        // Note OutputBufferLength = MmGetMdlByteCount(Irp->MdlAddress)
        //

        WdfRequestSetInformation(Request, OutputBufferLength);

        //
        // NOTE: Changes made to the  SystemBuffer are not copied
        // to the user input buffer by the I/O manager
        //

      break;

    case IOCTL_NONPNP_METHOD_OUT_DIRECT:


        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_OUT_DIRECT\n");

        //
        // Get the Input buffer. WdfRequestRetrieveInputBuffer returns
        // Irp->AssociatedIrp.SystemBuffer.
        //
        status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize);
        if(!NT_SUCCESS(status)) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        ASSERT(bufSize == InputBufferLength);

        Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data from User : %!HEXDUMP!\n",
                        log_xstr(inBuf, (USHORT)InputBufferLength)));
        PrintChars(inBuf, InputBufferLength);

        //
        // Get the output buffer. Framework calls MmGetSystemAddressForMdlSafe
        // on the Irp->MdlAddress and returns the system address.
        // For this method, this buffer is intended for transfering data from the
        // driver to the application.
        //
        status = WdfRequestRetrieveOutputBuffer(Request, 0, &buffer, &bufSize);
        if(!NT_SUCCESS(status)) {
            break;
        }

        ASSERT(bufSize == OutputBufferLength);

        //
        // Write data to be sent to the user in this buffer
        //
        RtlCopyMemory(buffer, data, OutputBufferLength);

        Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data to User : %!HEXDUMP!\n",
                        log_xstr(buffer, (USHORT)datalen)));
        PrintChars(buffer, datalen);

        WdfRequestSetInformation(Request,
                    OutputBufferLength < datalen? OutputBufferLength: datalen);

        //
        // NOTE: Changes made to the  SystemBuffer are not copied
        // to the user input buffer by the I/O manager
        //

        break;

    case IOCTL_NONPNP_METHOD_NEITHER:
        {
            size_t inBufLength, outBufLength;

            //
            // The NonPnpEvtDeviceIoInCallerContext has already probe and locked the
            // pages and mapped the user buffer into system address space and
            // stored memory buffer pointers in the request context. We can get the
            // buffer pointer by calling WdfMemoryGetBuffer.
            //
            TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_NEITHER\n");

            reqContext = GetRequestContext(Request);

            inBuf = WdfMemoryGetBuffer(reqContext->InputMemoryBuffer, &inBufLength);
            outBuf = WdfMemoryGetBuffer(reqContext->OutputMemoryBuffer, &outBufLength);

            if(inBuf == NULL || outBuf == NULL) {
                status = STATUS_INVALID_PARAMETER;
            }

            ASSERT(inBufLength == InputBufferLength);
            ASSERT(outBufLength == OutputBufferLength);

            //
            // Now you can safely read the data from the buffer in any arbitrary
            // context.
            //
            Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data from User : %!HEXDUMP!\n",
                            log_xstr(inBuf, (USHORT)inBufLength)));
            PrintChars(inBuf, inBufLength);

            //
            // Write to the buffer in any arbitrary context.
            //
            RtlCopyMemory(outBuf, data, outBufLength);

            Hexdump((TRACE_LEVEL_VERBOSE,  DBG_IOCTL, "Data to User : %!HEXDUMP!\n",
                            log_xstr(outBuf, (USHORT)datalen)));
            PrintChars(outBuf, datalen);

            //
            // Assign the length of the data copied to IoStatus.Information
            // of the Irp and complete the Irp.
            //
            WdfRequestSetInformation(Request,
                    outBufLength < datalen? outBufLength:datalen);

            break;
        }
    default:

        //
        // The specified I/O control code is unrecognized by this driver.
        //
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ERROR: unrecognized IOCTL %x\n", IoControlCode);
        break;
    }

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Completing Request %p with status %X",
                   Request, status );

    WdfRequestComplete( Request, status);

}
Exemple #7
0
// Main function --------------------------------------------------------------
int main(int argc, char *argv[]) {

	int c;
	extern char *optarg;
	char *deviceSymbolicName = NULL;
	char *singleIoctl		 = NULL;
	char *rangeIoctl		 = NULL;
	int singleflg  = 0;
	int errflg 	 = 0;
	int quietflg = 0;
	int displayerrflg = 0;
	int filteralwaysok = 0;
	
	HANDLE deviceHandle;
	char   deviceName[100] = "\\\\.\\";
	DWORD  beginIoctl, endIoctl, currentIoctl;
	DWORD  status, errorCode;
	DWORD  nbBytes = 0;
	
	pIOCTLlist listIoctls 	 = NULL;
	pIOCTLlist posListIoctls = NULL;
	
	int choice = -1;
	unsigned int i,j;   
	int fuzzData;
		
	BYTE  bufInput[0x10000];
	BYTE  bufOutput[0x10000];
	size_t randomLength;
	
	
	// Parse options from command-line
	while((c = getopt(argc, argv, "d:i:r:uqh?ef")) != -1) {
		switch(c) {
			case 'd':
				deviceSymbolicName = optarg;
				break;
			case 'i':
				if(rangeIoctl)
					errflg++;
				else
					singleIoctl = optarg;
				break;
			case 'r':
				if(singleIoctl)
					errflg++;
				else
					rangeIoctl = optarg;
				break;
			case 'u':
				if(rangeIoctl)
					errflg++;
				singleflg = 1;
				break;
			case 'q':
				quietflg++;
				break;
			case 'e':
				displayerrflg++;
				break;
			case 'f':
				filteralwaysok++;
				break;
			case 'h':
			case '?':
				errflg++;
		}
	}
	
	// Check & parse options from command line
	if(deviceSymbolicName == NULL || (rangeIoctl == NULL && singleIoctl == NULL))
		errflg++;
	
	if(!errflg) {
		// IOCTL range mode
		if(rangeIoctl) {
			if(strchr(rangeIoctl, '-') == NULL)
				errflg++;
			else {
				beginIoctl 	= (DWORD)parseHex(strtok(rangeIoctl, "-"));
				endIoctl	= (DWORD)parseHex(strtok(NULL, "-"));
				if(endIoctl < beginIoctl)
					errflg++;
			}
		}
		// Function code + Transfer type (14 lowest bits) bruteforce mode
		else if(singleIoctl && !singleflg) {
			beginIoctl = (DWORD)parseHex(singleIoctl) & 0xffffc000;
			endIoctl   = ((DWORD)parseHex(singleIoctl) & 0xffffc000) | 0x00003fff;			
		}
		// Single IOCTL mode
		else {
			beginIoctl 	= (DWORD)parseHex(singleIoctl);
			endIoctl	= beginIoctl;
		}
	}
	
	// Print usage if necessary
	if(errflg)
		usage(argv[0]);
					

	banner();
	
	// Open handle to the device
	strncat(deviceName, deviceSymbolicName, 90); 
	printf("[~] Open handle to the device %s ... ", deviceName);
	deviceHandle = CreateFile((HANDLE)deviceName, 
							  GENERIC_READ, 
							  0, 
							  NULL, 
							  OPEN_EXISTING, 
							  0, 
							  NULL);
	if(deviceHandle == INVALID_HANDLE_VALUE) {
		printf("FAILED, error code: %d\n%s\n", GetLastError(), 
										errorCode2String(GetLastError()));
		exit(1);
	}
	printf("OK\n\n");
	
	memset(bufInput,  0x00, 0x10000);
	memset(bufOutput, 0x00, 0x10000);
	
	
	// Print summary	
	printf("  Summary                             	\n");
	printf("  -------								\n");
	printf("  IOCTL scanning mode 	: ");
	if(rangeIoctl)
		printf("Range mode 0x%08x - 0x%08x\n", beginIoctl, endIoctl);
	else if(singleIoctl && singleflg)
		printf("Single mode 0x%08x\n", beginIoctl);
	else
		printf("Function + transfer type bf 0x%08x - 0x%08x\n", 
													   beginIoctl, endIoctl);
	printf("  Filter mode           : ");
	if(filteralwaysok)
		printf("Filter codes that return true for all buffer sizes\n");
	else
		printf("Filter disabled\n");

	printf("  Symbolic Device Name  : %s\n", deviceName);
	if(singleIoctl)
		printf("  Device Type    	: 0x%08x\n", 
						(beginIoctl & 0xffff0000) >> 16);
	printf("  Device handle         : 0x%08x\n", deviceHandle);
	printf("\n");
	
	
	// IOCTL code scanning
	if(singleIoctl && singleflg)
		printf("[~] Test given IOCTL and determine input size...\n");
	else
		printf("[~] Bruteforce function code + transfer type and determine "
		       "input sizes...\n");

	
	i = 0;
	for(currentIoctl = beginIoctl; currentIoctl<=endIoctl; currentIoctl++) {
		
		if(!singleflg && !displayerrflg && currentIoctl % 0x400 == 0)
			printf(".");
			
		// DeviceIoControl: if the operation completes successfully, the 
		// return value is nonzero
		status = DeviceIoControl(deviceHandle, 
								 currentIoctl, 
								 NULL,
								 0,
								 NULL, 
								 0, 
								 &nbBytes, 
								 NULL);
	
		// No further tests for the current IOCTL if the operation fails with 
		// one of the following error codes:
		// - ERROR_INVALID_FUNCTION		0x1
		// - ERROR_ACCESS_DENIED		0x5
		// - ERROR_NOT_SUPPORTED		0x50
		// cf. winerror.h
		if(status == 0) {
			errorCode = GetLastError();
						
			// -- DEBUG
			//if(errorCode != 87)
			if(displayerrflg) {
				printf("0x%08x -> error code %03d - %s\n", currentIoctl, 
				       errorCode, errorCode2String(errorCode));
			}
			
			//printf("0x%08x -> code %d\n", currentIoctl, errorCode);
			// errorCode == ERROR_INVALID_FUNCTION || 
			if(errorCode == ERROR_ACCESS_DENIED    || 
			   errorCode == ERROR_NOT_SUPPORTED)
				continue;
		}
		
		// Filter out IOCTLs that always return status != 0
		if(filteralwaysok) {
			status = DeviceIoControl(deviceHandle, 
									currentIoctl, 
									&bufInput, 
									MAX_BUFSIZE, 
									&bufOutput, 
									MAX_BUFSIZE, 
									&nbBytes, 
									NULL);
			if(status != 0) {
				cont   = TRUE;
				status = 1; 
				for(j=0; j<4 && status != 0 && cont; j++) {
					status = DeviceIoControl(deviceHandle, 
										 currentIoctl, 
										 &bufInput, 
										 j, 
										 &bufOutput, 
										 j,
										 &nbBytes, 
										 NULL);	
					
					/*
					if(status == 0)
						printf("0x%08x (size %d) -> error code %03d \n", currentIoctl, j, GetLastError());
					else 
						printf("0x%08x (size %d) -> status != 0 \n", currentIoctl, j);
					*/
					
				}
				if(j == 4) {
					//printf("Skip 0x%08x\n", currentIoctl);
					continue;
				}
			}
		}
									
		// Determine min/max input buffer size
		cont = TRUE;
		for(j=0; j<MAX_BUFSIZE && cont; j++) {
			status = DeviceIoControl(deviceHandle, 
									 currentIoctl, 
									 &bufInput, 
									 j, 
									 &bufOutput, 
									 j,
									 &nbBytes, 
									 NULL);

			if(status != 0) {
				listIoctls = addIoctlList(listIoctls, 
										  currentIoctl, 
										  0, 
										  j, 
										  MAX_BUFSIZE);
				cont = FALSE;
				i++;
			}
			/*
			else {
				// DEBUG
				if(GetLastError() != 31)
					printf("Size = %04x -> code %d\n", j, GetLastError());
			}
			*/
			
		}
		if(!cont) {
			// Ok, the min buffer size has been found. Let's find the max size
			cont = TRUE;
			status = DeviceIoControl(deviceHandle, 
									 currentIoctl, 
									 &bufInput, 
									 MAX_BUFSIZE, 
									 &bufOutput, 
									 MAX_BUFSIZE, 
									 &nbBytes, 
									 NULL);
			if(status != 0) {
				listIoctls->maxBufferLength = MAX_BUFSIZE;
				cont = FALSE;
			}
			
			for(j=listIoctls->minBufferLength+1; 
			    j<MAX_BUFSIZE && cont; j++) {
				status = DeviceIoControl(deviceHandle, 
									     currentIoctl, 
										 &bufInput, 
										 j, 
										 &bufOutput, 
										 j, 
										 &nbBytes, 
										 NULL);
				if(status == 0) {
					listIoctls->maxBufferLength = j-1;
					cont = FALSE;
				}
			}
			if(cont) {
				listIoctls->maxBufferLength = MAX_BUFSIZE;
			}
		}
		/*
		else {
			// If we're here, it means no min input buffer size has been found
			// DEBUG -----
			printf("No min bufsize found for IOCTL 0x%08x\n", currentIoctl);
			//listIoctls = addIoctlList(listIoctls, currentIoctl, 
			//GetLastError(), 0, MAX_BUFSIZE);
			//i++;
		}
		*/
	}
	printf("\n");
	if(i == 0) {
		if(singleflg)
			printf("[!] Given IOCTL code seems not to be recognized by the "
			       "driver !\n");
		else
			printf("[!] No valid IOCTL code has been found !\n");
		exit(1);
	}
	else {
		if(singleflg)
			printf("[!] Given IOCTL code is recognized by the driver !\n\n");
		else
			printf("[+] %d valid IOCTL have been found\n\n", i);
	}
	
	
	// Fuzzing IOCTL buffer
	while(1) {
	
		// Choice of the IOCTL to fuzz
		printf("  Valid IOCTLs found \n");
		printf("  ------------------ \n");
		printIoctlList(listIoctls, MAX_BUFSIZE);
		printf("\n");
		
		if(singleflg) {
			choice  = 0;
		}
		else {
			printf("[?] Choose an IOCTL to fuzz...\n");
			printIoctlChoice(listIoctls);
			printf("Choice : ");
			scanf_s("%d", &choice, 3);
			
			if(choice < 0 || choice >= getIoctlListLength(listIoctls))
				continue;
		}
		
		
		posListIoctls = getIoctlListElement(listIoctls, choice);
		
		// Start fuzzing
		printf("\n");
		printf("  FuzZing IOCTL 0x%08x     \n", posListIoctls->IOCTL);
		printf("  ------------------------ \n");

		
		// --------------------------------------------------------------------
		// Stage 1: Check for invalid addresses of buffer 
		// (for method != METHOD_BUFFERED)
		if((posListIoctls->IOCTL & 0x00000003) != 0) {
			printf("[0x%08x] Checking for invalid addresses of in/out buffers...",
				   posListIoctls->IOCTL);
			getch();
			printf("\n");
			cont = TRUE;
			for(i=0; cont && i<INVALID_BUF_ADDR_ATTEMPTS; i++) {
				for(j=0; cont && j<(sizeof(invalidAddresses)/4); j++) {
					// Choose a random length for the buffer
					randomLength = getrand(posListIoctls->minBufferLength, 
										   posListIoctls->maxBufferLength);
										   
					status = DeviceIoControl(deviceHandle, 
											 posListIoctls->IOCTL, 
											 (LPVOID)invalidAddresses[j], 
											 randomLength,
											 (LPVOID)invalidAddresses[j], 
											 randomLength, 
											 &nbBytes, 
											 NULL);
					Sleep(SLEEP_TIME);
				}
				printf(".");
			}
			printf("DONE\n\n");
		}
		
		
		// --------------------------------------------------------------------
		// Stage 2: Check for trivial kernel overflow
		printf("[0x%08x] Checking for trivial kernel overflows ...", 
			   posListIoctls->IOCTL);
		getch();
		printf("\n");
		cont = TRUE;
		memset(bufInput, 0x41, 0x10000);
		for(i=0x100; i<=0x10000; i+=0x100) {
			if(i % 0x1000 == 0)
				printf(".");
			status = DeviceIoControl(deviceHandle, 
									 posListIoctls->IOCTL, 
									 &bufInput, 
									 i, 
			                         &bufOutput, 
									 i, 
									 &nbBytes, 
									 NULL);
			Sleep(SLEEP_TIME);
		}
		memset(bufInput, 0x00, 0x10000);
		printf("DONE\n\n");

		
		// --------------------------------------------------------------------
		// Stage 3: Fuzzing with predetermined DWORDs
		printf("[0x%08x] Fuzzing with predetermined DWORDs, max buffer size...\n", 
			   posListIoctls->IOCTL);
		printf("(Ctrl+C to pass to the next step)");
		getch();
		printf("\n");
		cont = TRUE;
		if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) {
		
			// Fill the buffer with data from FuzzConstants (1 DWORD after 1)
			memset(bufInput, 0x00, MAX_BUFSIZE);
			for(i=0; cont && i<posListIoctls->maxBufferLength; i=i+4) {
			
				printf("Fuzzing DWORD %d/%d\n", 
					   i/4+1, posListIoctls->maxBufferLength/4);
				
				// Fill the whole buffer with random data...
				for(j=0; cont && j<posListIoctls->maxBufferLength; j++) {
					bufInput[j] = (BYTE)getrand(0x00, 0xff);
				}
				
				// ...and put a DWORD from FuzzConstants at the i_th position
				for(j=0; cont && j<(sizeof(FuzzConstants)/4); j++) {
					fuzzData = FuzzConstants[j];
					
					/*
					printf("Fuzzing DWORD %d/%d with 0x%08x (%d/%d)\n", 
						   i/4+1, posListIoctls->maxBufferLength/4, 
						   fuzzData, j+1, sizeof(FuzzConstants)/4);
					*/
													
					// Choose a random element into FuzzConstants
					bufInput[i]   = fuzzData & 0x000000ff;
					bufInput[i+1] = (fuzzData & 0x0000ff00) >> 8;
					bufInput[i+2] = (fuzzData & 0x00ff0000) >> 16;
					bufInput[i+3] = (fuzzData & 0xff000000) >> 24;
					
					if(!quietflg) {
						Hexdump(bufInput, posListIoctls->maxBufferLength);
						printf("Fuzzing DWORD %d/%d with 0x%08x (%d/%d)\n", 
						       i/4+1, posListIoctls->maxBufferLength/4, 
						       fuzzData, j+1, sizeof(FuzzConstants)/4);
						printf("Input buffer: %d (0x%x) bytes \n", 
						                     posListIoctls->maxBufferLength,
							                 posListIoctls->maxBufferLength);
					}
					
					status = DeviceIoControl(deviceHandle, 
											 posListIoctls->IOCTL, 
											 &bufInput, 
											 posListIoctls->maxBufferLength,
											 &bufOutput, 
											 posListIoctls->maxBufferLength, 
											 &nbBytes, 
											 NULL);
											 
					if(!quietflg) {
						if(status == 0)
							printf("Error %d: %s\n\n", GetLastError(), 
							                 errorCode2String(GetLastError()));
						printf("-------------------------------------------------------------------\n\n");
					}
					
					Sleep(SLEEP_TIME);
				}
			}
			
			printf("Filling the whole buffer with predetermined DWORDs\n");
			while(cont) {
				// Choose a random length for the buffer
				randomLength = getrand(posListIoctls->minBufferLength, 
				                       posListIoctls->maxBufferLength);
				
				// Fill the whole buffer with data from FuzzConstants
				memset(bufInput, 0x00, MAX_BUFSIZE);
				for(i=0; i<randomLength; i=i+4) {
					fuzzData = FuzzConstants[getrand(0, (sizeof(FuzzConstants)/4)-1)];
														
					// Choose a random element into FuzzConstants
					bufInput[i]   = fuzzData & 0x000000ff;
					bufInput[i+1] = (fuzzData & 0x0000ff00) >> 8;
					bufInput[i+2] = (fuzzData & 0x00ff0000) >> 16;
					bufInput[i+3] = (fuzzData & 0xff000000) >> 24;
				}
				
				if(!quietflg) {
					Hexdump(bufInput, randomLength);
					printf("Filling the whole buffer with predetermined DWORDs\n");
					printf("Input buffer: %d (0x%x) bytes \n", randomLength,
															   randomLength);
				}

				status = DeviceIoControl(deviceHandle, 
										 posListIoctls->IOCTL, 
										 &bufInput, 
										 randomLength,
										 &bufOutput, 
										 randomLength, 
										 &nbBytes, 
										 NULL);
										 
				if(!quietflg) {
					if(status == 0)
						printf("Error %d: %s\n\n", GetLastError(), errorCode2String(GetLastError()));
					printf("-------------------------------------------------------------------\n\n");
				}
										 
				Sleep(SLEEP_TIME);	
			}
				
		}
		else {
Exemple #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() );
    //}

    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 );
}
int RemoteCameraRtsp::Capture( Image &image )
{
	AVPacket packet;
	uint8_t* directbuffer;
	int frameComplete = false;
	
	/* Request a writeable buffer of the target image */
	directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
	if(directbuffer == NULL) {
		Error("Failed requesting writeable buffer for the captured image.");
		return (-1);
	}
	
    while ( true )
    {
        buffer.clear();
        if ( !rtspThread->isRunning() )
            return (-1);

        if ( rtspThread->getFrame( buffer ) )
        {
            Debug( 3, "Read frame %d bytes", buffer.size() );
            Debug( 4, "Address %p", buffer.head() );
            Hexdump( 4, buffer.head(), 16 );

            if ( !buffer.size() )
                return( -1 );

            if(mCodecContext->codec_id == AV_CODEC_ID_H264)
            {
                // SPS and PPS frames should be saved and appended to IDR frames
                int nalType = (buffer.head()[3] & 0x1f);
                
                // SPS
                if(nalType == 7)
                {
                    lastSps = buffer;
                    continue;
                }
                // PPS
                else if(nalType == 8)
                {
                    lastPps = buffer;
                    continue;
                }
                // IDR
                else if(nalType == 5)
                {
                    buffer += lastSps;
                    buffer += lastPps;
                }
            }

            av_init_packet( &packet );
            
	    while ( !frameComplete && buffer.size() > 0 )
	    {
		packet.data = buffer.head();
		packet.size = buffer.size();
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
		int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
#else
		int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
#endif
		if ( len < 0 )
		{
			Error( "Error while decoding frame %d", frameCount );
			Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
			buffer.clear();
			continue;
		}
		Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
		//if ( buffer.size() < 400 )
		   //Hexdump( 0, buffer.head(), buffer.size() );
		   
		buffer -= len;

	    }
            if ( frameComplete ) {
	       
		Debug( 3, "Got frame %d", frameCount );
			    
		avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
			
#if HAVE_LIBSWSCALE
		if(mConvertContext == NULL) {
			if(config.cpu_extensions && sseversion >= 20) {
				mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL );
			} else {
				mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
			}
			if(mConvertContext == NULL)
				Fatal( "Unable to create conversion context");
		}
	
		if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
			Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
#else // HAVE_LIBSWSCALE
		Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
#endif // HAVE_LIBSWSCALE
	
		frameCount++;

	     } /* frame complete */
	     
	     av_free_packet( &packet );
	} /* getFrame() */
 
	if(frameComplete)
		return (0);
	
    }
    return (0) ;
}
/**
* @brief 
*
* @param buffer
*
* @return 
*/
bool RtspConnection::recvRequest( ByteBuffer &buffer )
{
    Debug( 2, "Received RTSP message, %zd bytes", buffer.size() );

    while( !buffer.empty() )
    {
        if ( buffer[0] == '$' || (!mRequest.empty() && mRequest[0] == '$') )
        {
            // Interleaved request
            if ( mRequest )
            {
                buffer = mRequest+buffer;
                mRequest.clear();
                Debug( 6, "Merging with saved request, total bytes: %zd", buffer.size() );
            }
            Debug( 2, "Got RTP interleaved request" );
            if ( buffer.size() <= 4 )
            {
                Debug( 6, "Request is incomplete, storing" );
                mRequest = buffer;
                return( true );
            }
            //int channel= buffer[1];
            uint16_t rawLength;
            memcpy( &rawLength, &buffer[2], sizeof(rawLength) );
            uint16_t length = be16toh( rawLength );
            if ( (length+2) > buffer.size() )
            {
                Debug( 6, "Request is incomplete, storing" );
    Hexdump( 2, buffer.data(), buffer.size() );
                mRequest = buffer;
                return( true );
            }
            ByteBuffer request = buffer.range( 0, 4+length );
            buffer.consume( 4+length );
            if ( !handleRequest( request ) )
                return( false );
        }
        else
        {
            Debug( 2, "Got RTSP request" );

            std::string request( reinterpret_cast<const char *>(buffer.data()), buffer.size() );

            if ( mRequest.size() > 0 )
            {
                //request = mRequest+request;
                request = std::string( reinterpret_cast<const char *>(mRequest.data()), mRequest.size() )+request;
                mRequest.clear();
                Debug( 6, "Merging with saved request, total: %s", request.c_str() );
            }

            const char *endOfMessage = strstr( request.c_str(), "\r\n\r\n" );

            if ( !endOfMessage )
            {
                Debug( 6, "Request '%s' is incomplete, storing", request.c_str() );
                mRequest = ByteBuffer( reinterpret_cast<const unsigned char *>(request.data()), request.size() );
                return( true );
            }

            size_t messageLen = endOfMessage-request.c_str();
            request.erase( messageLen );
            buffer.consume( messageLen+4 );
            if ( !handleRequest( request ) )
                return( false );
        }
    }
    return( true );
}
int RemoteCameraRtsp::Capture( Image &image )
{
    while ( true )
    {
        buffer.clear();
        if ( !rtspThread->isRunning() )
            break;
        //if ( rtspThread->stopped() )
            //break;
        if ( rtspThread->getFrame( buffer ) )
        {
            Debug( 3, "Read frame %d bytes", buffer.size() );
            Debug( 4, "Address %p", buffer.head() );
            Hexdump( 4, buffer.head(), 16 );

            static AVFrame *tmp_picture = NULL;

            if ( !tmp_picture )
            {
                //if ( c->pix_fmt != pf )
                //{
                    tmp_picture = avcodec_alloc_frame();
                    if ( !tmp_picture )
                    {
                        Panic( "Could not allocate temporary opicture" );
                    }
                    int size = avpicture_get_size( PIX_FMT_RGB24, width, height);
                    uint8_t *tmp_picture_buf = (uint8_t *)malloc(size);
                    if (!tmp_picture_buf)
                    {
                        av_free( tmp_picture );
                        Panic( "Could not allocate temporary opicture" );
                    }
                    avpicture_fill( (AVPicture *)tmp_picture, tmp_picture_buf, PIX_FMT_RGB24, width, height );
                //}
            }

            if ( !buffer.size() )
                return( -1 );

            AVPacket packet;
            av_init_packet( &packet );
            int initialFrameCount = frameCount;
            while ( buffer.size() > 0 )
            {
                int got_picture = false;
                packet.data = buffer.head();
                packet.size = buffer.size();
                int len = avcodec_decode_video2( codecContext, picture, &got_picture, &packet );
                if ( len < 0 )
                {
                    if ( frameCount > initialFrameCount )
                    {
                        // Decoded at least one frame
                        return( 0 );
                    }
                    Error( "Error while decoding frame %d", frameCount );
                    Hexdump( ZM_DBG_ERR, buffer.head(), buffer.size()>256?256:buffer.size() );
                    buffer.clear();
                    continue;
                    //return( -1 );
                }
                Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
                //if ( buffer.size() < 400 )
                    //Hexdump( 0, buffer.head(), buffer.size() );

                if ( got_picture )
                {
                    /* the picture is allocated by the decoder. no need to free it */
                    Debug( 1, "Got picture %d", frameCount );

                    static struct SwsContext *img_convert_ctx = 0;

                    if ( !img_convert_ctx )
                    {
                        img_convert_ctx = sws_getCachedContext( NULL, codecContext->width, codecContext->height, codecContext->pix_fmt, width, height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL );
                        if ( !img_convert_ctx )
                            Panic( "Unable to initialise image scaling context" );
                    }

                    sws_scale( img_convert_ctx, picture->data, picture->linesize, 0, height, tmp_picture->data, tmp_picture->linesize );

                    image.Assign( width, height, colours, tmp_picture->data[0] );

                    frameCount++;

                    return( 0 );
                }
                else
                {
                    Warning( "Unable to get picture from frame" );
                }
                buffer -= len;
            }
        }
    }
    return( -1 );
}
VOID
NICServiceReadIrps(
    PFDO_DATA   FdoData,
    PMP_RFD     *PacketArray,
    ULONG       PacketArrayCount
    )
/*++
Routine Description:

    Copy the data from the recv buffers to pending read IRP buffers
    and complete the IRP. When used as network driver, copy operation
    can be avoided by devising a private interface between us and the
    NDIS-WDM filter and have the NDIS-WDM edge to indicate our buffers
    directly to NDIS.

    Called at DISPATCH_LEVEL. Take advantage of that fact while
     acquiring spinlocks.

Arguments:

    FdoData     Pointer to our FdoData

Return Value:

     None

--*/
{
    PMP_RFD             pMpRfd = NULL;
    ULONG               index;
    NTSTATUS            status;
    PVOID               buffer;
    WDFREQUEST          request;
    size_t              bufLength=0;

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> NICServiceReadIrps\n");


    for(index=0; index < PacketArrayCount; index++)
    {
        pMpRfd = PacketArray[index];
        ASSERT(pMpRfd);

        status = WdfIoQueueRetrieveNextRequest( FdoData->PendingReadQueue,
                                                &request );

        if(NT_SUCCESS(status)){

            WDF_REQUEST_PARAMETERS  params;
            ULONG                   length = 0;

            WDF_REQUEST_PARAMETERS_INIT(&params);

            WdfRequestGetParameters(
                request,
                &params
                 );

            ASSERT(status == STATUS_SUCCESS);

            bufLength = params.Parameters.Read.Length;

            status = WdfRequestRetrieveOutputBuffer(request,
                                                    bufLength,
                                                    &buffer,
                                                    &bufLength);
            if(NT_SUCCESS(status) ) {

                length = min((ULONG)bufLength, pMpRfd->PacketSize);

                RtlCopyMemory(buffer, pMpRfd->Buffer, length);

                Hexdump((TRACE_LEVEL_VERBOSE, DBG_READ,
                         "Received Packet Data: %!HEXDUMP!\n",
                         log_xstr(buffer, (USHORT)length)));
                FdoData->BytesReceived += length;
            }

            WdfRequestCompleteWithInformation(request, status, length);
        }else {
            ASSERTMSG("WdfIoQueueRetrieveNextRequest failed",
                      (status == STATUS_NO_MORE_ENTRIES ||
                       status == STATUS_WDF_PAUSED));
        }

        WdfSpinLockAcquire(FdoData->RcvLock);

        ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_PEND));
        MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_PEND);


        if (FdoData->RfdShrinkCount < NIC_RFD_SHRINK_THRESHOLD)
        {
            NICReturnRFD(FdoData, pMpRfd);
        }
        else
        {
            ASSERT(FdoData->CurrNumRfd > FdoData->NumRfd);
            status = PciDrvQueuePassiveLevelCallback(FdoData,
                                    NICFreeRfdWorkItem, (PVOID)pMpRfd,
                                    NULL);
            if(NT_SUCCESS(status)){

                FdoData->RfdShrinkCount = 0;
                FdoData->CurrNumRfd--;
                TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Shrink... CurrNumRfd = %d\n",
                                                    FdoData->CurrNumRfd);
            } else {
                //
                // We couldn't queue a workitem to free memory, so let us
                // put that back in the main pool and try again next time.
                //
                NICReturnRFD(FdoData, pMpRfd);
            }
        }


        WdfSpinLockRelease(FdoData->RcvLock);

    }// end of loop

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- NICServiceReadIrps\n");

    return;

}