/** * @brief * * @return */ int FilterSwapUV::run() { if ( waitForProviders() ) { uint16_t inputWidth = videoProvider()->width(); uint16_t inputHeight = videoProvider()->height(); PixelFormat inputPixelFormat = videoProvider()->pixelFormat(); ByteBuffer tempBuffer; int yChannelSize = inputWidth*inputHeight; int uvChannelSize = yChannelSize/4; if ( inputPixelFormat != PIX_FMT_YUV420P ) Fatal( "Can't swap UV for pixel format %d", inputPixelFormat ); while ( !mStop ) { mQueueMutex.lock(); if ( !mFrameQueue.empty() ) { Debug( 3, "Got %zd frames on queue", mFrameQueue.size() ); for ( FrameQueue::iterator iter = mFrameQueue.begin(); iter != mFrameQueue.end(); iter++ ) { //const VideoFrame *frame = dynamic_cast<const VideoFrame *>(iter->get()); //FramePtr framePtr( *iter ); const FeedFrame *frame = (*iter).get(); Debug(1, "%s / Provider: %s, Source: %s, Frame: %p (%ju / %.3f) - %lu", cname(), frame->provider()->cidentity(), frame->originator()->cidentity(), frame, frame->id(), frame->age(), frame->buffer().size() ); //Image image( inputPixelFormat, inputWidth, inputHeight, frame->buffer().data() ); tempBuffer.size( frame->buffer().size() ); memcpy( tempBuffer.data(), frame->buffer().data(), yChannelSize ); memcpy( tempBuffer.data()+yChannelSize, frame->buffer().data()+yChannelSize+uvChannelSize, uvChannelSize); memcpy( tempBuffer.data()+yChannelSize+uvChannelSize, frame->buffer().data()+yChannelSize, uvChannelSize); VideoFrame *videoFrame = new VideoFrame( this, *iter, mFrameCount, frame->timestamp(), tempBuffer ); distributeFrame( FramePtr( videoFrame ) ); //delete *iter; mFrameCount++; } mFrameQueue.clear(); } mQueueMutex.unlock(); checkProviders(); // Quite short so we can always keep up with the required packet rate for 25/30 fps usleep( INTERFRAME_TIMEOUT ); } } FeedProvider::cleanup(); FeedConsumer::cleanup(); return( !ended() ); }
/** * @brief * * @return */ int ImageTimestamper::run() { if ( waitForProviders() ) { uint16_t inputWidth = videoProvider()->width(); uint16_t inputHeight = videoProvider()->height(); PixelFormat inputPixelFormat = videoProvider()->pixelFormat(); while ( !mStop ) { mQueueMutex.lock(); if ( !mFrameQueue.empty() ) { Debug( 3, "Got %zd frames on queue", mFrameQueue.size() ); for ( FrameQueue::iterator iter = mFrameQueue.begin(); iter != mFrameQueue.end(); iter++ ) { //const VideoFrame *frame = dynamic_cast<const VideoFrame *>(iter->get()); //FramePtr framePtr( *iter ); const FeedFrame *frame = (*iter).get(); Debug(1, "%s / Provider: %s, Source: %s, Frame: %p (%ju / %.3f) - %lu", cname(), frame->provider()->cidentity(), frame->originator()->cidentity(), frame, frame->id(), frame->age(), frame->buffer().size() ); Image image( inputPixelFormat, inputWidth, inputHeight, frame->buffer().data() ); if ( timestampImage( &image, frame->timestamp() ) ) { VideoFrame *videoFrame = new VideoFrame( this, *iter, mFrameCount, frame->timestamp(), image.buffer() ); distributeFrame( FramePtr( videoFrame ) ); } else { distributeFrame( *iter ); } //delete *iter; mFrameCount++; } mFrameQueue.clear(); } mQueueMutex.unlock(); checkProviders(); // Quite short so we can always keep up with the required packet rate for 25/30 fps usleep( INTERFRAME_TIMEOUT ); } } FeedProvider::cleanup(); FeedConsumer::cleanup(); return( !ended() ); }
/** * @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... }
/** * @brief * * @return */ int ImageScale::run() { AVFrame *inputFrame = av_frame_alloc(); AVFrame *outputFrame = av_frame_alloc(); if ( waitForProviders() ) { uint16_t inputWidth = videoProvider()->width(); uint16_t inputHeight = videoProvider()->height(); PixelFormat pixelFormat = videoProvider()->pixelFormat(); mWidth = inputWidth * mScale; mHeight = inputHeight * mScale; // Prepare for image format and size conversions mScaleContext = sws_getContext( inputWidth, inputHeight, pixelFormat, mWidth, mHeight, pixelFormat, SWS_BILINEAR, NULL, NULL, NULL ); if ( !mScaleContext ) Fatal( "Unable to create scale context" ); Debug( 1,"Scaling from %d x %d -> %d x %d", inputWidth, inputHeight, mWidth, mHeight ); Debug( 1,"%d bytes -> %d bytes", avpicture_get_size( pixelFormat, inputWidth, inputHeight ), avpicture_get_size( pixelFormat, mWidth, mHeight ) ); // Make space for anything that is going to be output ByteBuffer outputBuffer; outputBuffer.size( avpicture_get_size( pixelFormat, mWidth, mHeight ) ); // To get offsets only avpicture_fill( (AVPicture *)outputFrame, outputBuffer.data(), pixelFormat, mWidth, mHeight ); while ( !mStop ) { mQueueMutex.lock(); if ( !mFrameQueue.empty() ) { Debug( 3, "Got %zd frames on queue", mFrameQueue.size() ); for ( FrameQueue::iterator iter = mFrameQueue.begin(); iter != mFrameQueue.end(); iter++ ) { //const VideoFrame *frame = dynamic_cast<const VideoFrame *>(iter->get()); //FramePtr framePtr( *iter ); const FeedFrame *frame = (*iter).get(); if ( mWidth != inputWidth || mHeight != inputHeight ) { // Requires conversion Debug( 1,"%s / Provider: %s, Source: %s, Frame: %p (%ju / %.3f) - %lu", cname(), frame->provider()->cidentity(), frame->originator()->cidentity(), frame, frame->id(), frame->age(), frame->buffer().size() ); avpicture_fill( (AVPicture *)inputFrame, frame->buffer().data(), pixelFormat, inputWidth, inputHeight ); // Reformat the input frame to fit the desired output format if ( sws_scale( mScaleContext, inputFrame->data, inputFrame->linesize, 0, inputHeight, outputFrame->data, outputFrame->linesize ) < 0 ) Fatal( "Unable to convert input frame (%dx%d) to output frame (%dx%d) at frame %ju", inputWidth, inputHeight, mWidth, mHeight, mFrameCount ); VideoFrame *videoFrame = new VideoFrame( this, *iter, mFrameCount, frame->timestamp(), outputBuffer ); distributeFrame( FramePtr( videoFrame ) ); } else { // Send it out 'as is' distributeFrame( *iter ); } //delete *iter; mFrameCount++; } mFrameQueue.clear(); } mQueueMutex.unlock(); checkProviders(); // Quite short so we can always keep up with the required packet rate for 25/30 fps usleep( INTERFRAME_TIMEOUT ); } FeedProvider::cleanup(); FeedConsumer::cleanup(); sws_freeContext( mScaleContext ); mScaleContext = NULL; } av_free( outputFrame ); av_free( inputFrame ); return( !ended() ); }