[Libav-user] WEBM recording problem

Ankush kale ankushkale1 at gmail.com
Sun Apr 6 16:12:39 CEST 2014


Hi, I am recording video in WEBM ( VP8 ), My program screenshots 3
times per sec & adds it to video. The problem is video plays faster,
instead of playing like 1,2,3.. it jumps like 1,3,5 & shows wrong
video length ( means if I recorded it for 1min it will show 44sec )

I am giving timestamp to every bitmap i add to video but still some problem.

c# part:

public static void StartRecording()
        {
            DateTime RecordingStartTime = DateTime.Now;
			vencoder.InitFile(filename, 1000, 3,
Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
			
			while(!Environment.HasShutdownStarted & !abort)
			{
				Bitmap snap = takescreeshot();
                vencoder.AddBitmap(snap, DateTime.Now - RecordingStartTime);
				Thread.sleep(300);
			}
			vencoder.Finish();
		}
		
C++/Cli part:

bool VideoEncoder::InitFile(String^ inputFile,int^ bitrate,int^
fps,int^ width,int ^height)
{
	bool res = false;

	// convert specified managed String to unmanaged string
	IntPtr ptr = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(
inputFile );
    wchar_t* nativeFileNameUnicode = (wchar_t*) ptr.ToPointer( );
    int utf8StringSize = WideCharToMultiByte( CP_UTF8, 0,
nativeFileNameUnicode, -1, NULL, 0, NULL, NULL );
    char* nativeFileName = new char[utf8StringSize];
    WideCharToMultiByte( CP_UTF8, 0, nativeFileNameUnicode, -1,
nativeFileName, utf8StringSize, NULL, NULL );

	const char * filename = nativeFileName;
	outputFilename = inputFile;
	
	SYSTEM_INFO sysinfo;
	GetSystemInfo( &sysinfo );
	DWORD numCPU = sysinfo.dwNumberOfProcessors;

  // Initialize libavcodec
  av_register_all();

  // Create format
  pOutFormat = av_guess_format(NULL, filename, NULL);
  AVCodec * avCodec = avcodec_find_encoder_by_name("libvpx");
  pOutFormat->video_codec=avCodec->id;
  //init shared variable
  BITRATE=(int)bitrate;
  FPS=(int)fps;
  WIDTH=(int)width;
  HEIGHT=(int)height;
  //

  if (pOutFormat)
  {
    // allocate context
    pFormatContext = avformat_alloc_context();
	//pFormatContext->bit_rate

    if (pFormatContext)
    {
      pFormatContext->oformat = pOutFormat;
      memcpy(pFormatContext->filename, filename, min(strlen(filename),
        sizeof(pFormatContext->filename)));

      // Add video and audio stream
      pVideoStream   = AddVideoStream(pFormatContext, pOutFormat->video_codec);

	  pVideoStream->codec->bit_rate=BITRATE; //2500; //5000
	  pVideoStream->codec->time_base.num=1;
	  pVideoStream->codec->time_base.den=FPS;
	  pVideoStream->codec->thread_count=numCPU;

      // Set the output parameters (must be done even if no
      // parameters).
      av_dump_format(pFormatContext, 0, filename, 1);

        // Open Video and Audio stream
        res = false;
        if (pVideoStream)
        {
          res = OpenVideo(pFormatContext, pVideoStream);
        }

        if (res && !(pOutFormat->flags & AVFMT_NOFILE))
        {
          if (avio_open(&pFormatContext->pb, filename, AVIO_FLAG_WRITE) < 0)
          {
            res = false;
            printf("Cannot open file\n");
          }
        }

        if (res)
        {
          avformat_write_header(pFormatContext,NULL);
		  pImgConvertCtx =
sws_getContext(pVideoStream->codec->width,pVideoStream->codec->height,PIX_FMT_GRAY8,pVideoStream->codec->width,pVideoStream->codec->height,PIX_FMT_YUV420P,SWS_FAST_BILINEAR,NULL,NULL,NULL);
//SWS_POINT //SWS_BICUBIC //SWS_FAST_BILINEAR
          res = true;
        }
      //}
    }
  }

  if (!res)
  {
    Free();
    printf("Cannot init file\n");
  }

  inputFile=nullptr;

  return res;
}

AVStream *VideoEncoder::AddVideoStream(AVFormatContext *pContext,
CodecID codec_id)
{
  AVCodecContext *pCodecCxt = NULL;
  AVStream *st    = NULL;

  st = av_new_stream(pContext, 0);
  if (!st)
  {
    printf("Cannot add new vidoe stream\n");
    return NULL;
  }

  pCodecCxt = st->codec;
  pCodecCxt->codec_id = (CodecID)codec_id;
  pCodecCxt->codec_type = AVMEDIA_TYPE_VIDEO;
  pCodecCxt->frame_number = 0;
  pCodecCxt->bit_rate = BITRATE;
  pCodecCxt->width  = WIDTH;
  pCodecCxt->height = HEIGHT;
  pCodecCxt->time_base.num = 1;
  pCodecCxt->time_base.den = FPS;
  pCodecCxt->pix_fmt = PIX_FMT_YUV420P;

  // Some formats want stream headers to be separate.
  if(pContext->oformat->flags & AVFMT_GLOBALHEADER)
  {
      pCodecCxt->flags |= CODEC_FLAG_GLOBAL_HEADER;
  }

  return st;
}

bool VideoEncoder::OpenVideo(AVFormatContext *oc, AVStream *pStream)
{
  AVCodec *pCodec;
  AVCodecContext *pContext;

  pContext = pStream->codec;

  // Find the video encoder.
  pCodec = avcodec_find_encoder(pContext->codec_id);
  if (!pCodec)
  {
    throw gcnew Exception( "Cannot find video codec." );
    return false;
  }

  // Open the codec.
  if (avcodec_open2(pContext, pCodec,NULL) < 0)
  {
	  throw gcnew Exception( "Cannot open video codec." );
      return false;
  }

  pVideoEncodeBuffer = NULL;
  if (!(pFormatContext->oformat->flags & AVFMT_RAWPICTURE))
  {
	nSizeVideoEncodeBuffer = WIDTH*HEIGHT*16;
    pVideoEncodeBuffer = (uint8_t *)av_malloc(nSizeVideoEncodeBuffer);
  }

  return true;
}

bool VideoEncoder::AddBitmap(Bitmap^ bitmap,TimeSpan timestamp)
{
	System::Drawing::Imaging::BitmapData^ bitmapData = bitmap->LockBits(
System::Drawing::Rectangle( 0, 0,bitmap->Width, bitmap->Height
),System::Drawing::Imaging::ImageLockMode::ReadOnly,System::Drawing::Imaging::PixelFormat::Format8bppIndexed);
	uint8_t* ptr = reinterpret_cast<uint8_t*>( static_cast<void*>(
bitmapData->Scan0 ) );
	uint8_t* srcData[4] = { ptr, NULL, NULL, NULL };
	int srcLinesize[4] = { bitmapData->Stride, 0, 0, 0 };
	
	pCurrentPicture = CreateFFmpegPicture(pVideoStream->codec->pix_fmt,
pVideoStream->codec->width, pVideoStream->codec->height);
	sws_scale(pImgConvertCtx, srcData, srcLinesize, 0, bitmap->Height,
pCurrentPicture->data, pCurrentPicture->linesize );

	bitmap->UnlockBits( bitmapData );

	if ( timestamp.Ticks >= 0 )
	{
		const double frameNumber = timestamp.TotalSeconds * FPS;
		pCurrentPicture->pts = static_cast<int64_t>( frameNumber );
	}

	write_video_frame();

	bitmapData=nullptr;
	ptr=NULL;

	return true;
}

AVFrame * VideoEncoder::CreateFFmpegPicture(int pix_fmt, int nWidth,
int nHeight)
{

  AVFrame *picture     = NULL;
  uint8_t *picture_buf = NULL;
  int size;

  picture = avcodec_alloc_frame();
  if ( !picture)
  {
    printf("Cannot create frame\n");
    return NULL;
  }

  size = avpicture_get_size((PixelFormat)pix_fmt, nWidth, nHeight);

  picture_buf = (uint8_t *) av_malloc(size);

  if (!picture_buf)
  {
    av_free(picture);
    printf("Cannot allocate buffer\n");
    return NULL;
  }

  avpicture_fill((AVPicture *)picture,
picture_buf,(PixelFormat)pix_fmt, nWidth, nHeight);

  return picture;
}

void VideoEncoder::write_video_frame() //set pcurrentpicture to frame to encode
{
	AVCodecContext* codecContext=NULL;

	codecContext = pVideoStream->codec;

	int out_size, ret = 0;

	if ( pFormatContext->oformat->flags & AVFMT_RAWPICTURE )
	{
		Console::WriteLine( "raw picture must be written" );
	}
	else
	{
		// encode the image
		out_size = avcodec_encode_video( codecContext,
pVideoEncodeBuffer,nSizeVideoEncodeBuffer, pCurrentPicture );

		// if zero size, it means the image was buffered
		if ( out_size > 0 )
		{
			AVPacket packet;
			av_init_packet( &packet );

			//if commented process explorer is visible now else ...
			if ( codecContext->coded_frame->pts != AV_NOPTS_VALUE )
			{
				packet.pts = av_rescale_q( codecContext->coded_frame->pts,
codecContext->time_base, pVideoStream->time_base );
			}

			if ( codecContext->coded_frame->pkt_dts != AV_NOPTS_VALUE )
			{
				packet.dts = av_rescale_q( codecContext->coded_frame->.pkt_dts,
codecContext->time_base, pVideoStream->time_base );
			}

			if ( codecContext->coded_frame->key_frame )
			{
				packet.flags |= AV_PKT_FLAG_KEY;
			}

			packet.stream_index = pVideoStream->index;
			packet.data = pVideoEncodeBuffer;
			packet.size = out_size;

			// write the compressed frame to the media file
			ret = av_interleaved_write_frame( pFormatContext, &packet );

			av_free_packet(&packet);
			av_free(pCurrentPicture->data[0]);
			av_free(pCurrentPicture);
			pCurrentPicture=NULL;
		}
		else
		{
			// image was buffered
		}
	}

	if ( ret != 0 )
	{
		throw gcnew Exception( "Error while writing video frame." );
	}
}

void VideoEncoder::CloseVideo(AVFormatContext *pContext, AVStream *pStream)
{
  avcodec_close(pStream->codec);
  if (pCurrentPicture)
  {
    if (pCurrentPicture->data)
    {
		av_free(pCurrentPicture->data[0]);
		pCurrentPicture->data[0] = NULL;
	}
    av_free(pCurrentPicture);
    pCurrentPicture = NULL;
  }

  if (pVideoEncodeBuffer)
  {
    av_free(pVideoEncodeBuffer);
    pVideoEncodeBuffer = NULL;
  }
  nSizeVideoEncodeBuffer = 0;
}

// Flushes delayed frames to disk
void VideoEncoder::Flush( )
{
	// This function goes by the data->VideoOutputBuffer extracting
	// and saving to disk one frame at time, using mostly the same
	// code which can be found on write_video_frame.

		int out_size, ret = 0;

		AVCodecContext* codecContext = pVideoStream->codec;
		
		while ( 1 ) // while there are still delayed frames
		{
			AVPacket packet;
		    av_init_packet(&packet);

			// attempt to extract a single delayed frame from the buffer
			out_size = avcodec_encode_video(codecContext, pVideoEncodeBuffer ,
nSizeVideoEncodeBuffer , NULL);

			if (out_size <= 0)
				break; // there are no more frames to be written

			// TODO: consider refactoring with write_video_frame?
			if ( codecContext->coded_frame->pts != AV_NOPTS_VALUE )
			{
				packet.pts = av_rescale_q( codecContext->coded_frame->pts,
codecContext->time_base, pVideoStream->time_base );
			}

			if ( codecContext->coded_frame->key_frame )
			{
				packet.flags |= AV_PKT_FLAG_KEY;
			}

			packet.stream_index = pVideoStream->index;
			packet.data = pVideoEncodeBuffer;
			packet.size = out_size;

			// write the compressed frame to the media file
			ret = av_interleaved_write_frame( pFormatContext, &packet );

			if ( ret != 0 )
			{
				throw gcnew Exception( "Error while writing video frame." );
			}
		}

		avcodec_flush_buffers(pVideoStream->codec);
}


bool VideoEncoder::Finish()
{
  bool res = true;

  Flush();

  if (pFormatContext)
  {
    av_write_trailer(pFormatContext);
    Free();
  }

  return res;
}

void VideoEncoder::Free()
{
  bool res = true;

  if (pFormatContext)
  {
    // close video stream
    if (pVideoStream)
    {
      CloseVideo(pFormatContext, pVideoStream);
    }

    // Free the streams.
    for(size_t i = 0; i < pFormatContext->nb_streams; i++)
    {
      av_freep(&pFormatContext->streams[i]->codec);
      av_freep(&pFormatContext->streams[i]);
    }

    if (!(pFormatContext->flags & AVFMT_NOFILE) && pFormatContext->pb)
    {
      avio_close(pFormatContext->pb);
    }

    // Free the stream.
    av_free(pFormatContext);
    pFormatContext = NULL;
	av_free(pImgConvertCtx);
  }
}

Tried too much times to correct this but not working. Guys please
suggest some fix

Another Que:
I am using NAudio, how can i add wave samples to vorbis audio to this?

I am developing free desktop recording program ( to generate Video Tutorials ).


More information about the Libav-user mailing list