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 ); }
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; }
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... }
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); }
// 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 {
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(¶ms); WdfRequestGetParameters( request, ¶ms ); 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; }