【问题标题】:libavcodec, how to transcode video with different frame rates?libavcodec,如何对不同帧率的视频进行转码?
【发布时间】:2011-05-10 16:57:38
【问题描述】:

我正在通过 v4l 从摄像头抓取视频帧,我需要将它们转码为 mpeg4 格式,以便通过 RTP 连续流式传输它们。

一切实际上都“有效”,但在重新编码时有些东西我没有:输入流产生 15fps,而输出为 25fps,并且每个输入帧都转换为一个视频对象序列(我验证了这一点对输出比特流的简单检查)。我猜接收器正在正确解析 mpeg4 比特流,但 RTP 打包在某种程度上是错误的。我应该如何将编码的比特流拆分为一个或多个 AVPacket ?也许我错过了明显的,我只需要寻找 B/P 帧标记,但我认为我没有正确使用编码 API。

这是我的代码摘录,基于可用的 ffmpeg 示例:

// input frame
AVFrame *picture;
// input frame color-space converted
AVFrame *planar;
// input format context, video4linux2
AVFormatContext *iFmtCtx;
// output codec context, mpeg4
AVCodecContext *oCtx;
// [ init everything ]
// ...
oCtx->time_base.num = 1;
oCtx->time_base.den = 25;
oCtx->gop_size = 10;
oCtx->max_b_frames = 1;
oCtx->bit_rate = 384000;
oCtx->pix_fmt = PIX_FMT_YUV420P;

for(;;)
{
  // read frame
  rdRes = av_read_frame( iFmtCtx, &pkt );
  if ( rdRes >= 0 && pkt.size > 0 )
  {
    // decode it
    iCdcCtx->reordered_opaque = pkt.pts;
    int decodeRes = avcodec_decode_video2( iCdcCtx, picture, &gotPicture, &pkt );
    if ( decodeRes >= 0 && gotPicture )
    {
      // scale / convert color space
      avpicture_fill((AVPicture *)planar, planarBuf.get(), oCtx->pix_fmt, oCtx->width, oCtx->height);
      sws_scale(sws, picture->data, picture->linesize, 0, iCdcCtx->height, planar->data, planar->linesize);
      // encode
      ByteArray encBuf( 65536 );
      int encSize = avcodec_encode_video( oCtx, encBuf.get(), encBuf.size(), planar );
      // this happens every GOP end
      while( encSize == 0 )
        encSize = avcodec_encode_video( oCtx, encBuf.get(), encBuf.size(), 0 );
      // send the transcoded bitstream with the result PTS
      if ( encSize > 0 )
        enqueueFrame( oCtx->coded_frame->pts, encBuf.get(), encSize );
    }
  }
}

【问题讨论】:

    标签: c++ c ffmpeg video-encoding libavcodec


    【解决方案1】:

    最简单的解决方案是使用两个线程。第一个线程将完成您问题中概述的所有事情(解码、缩放/色彩空间转换、编码)。部分转码的帧将被写入与第二个线程共享的中间队列。在这种特殊情况下(从较低比特率转换为较高比特率),此队列的最大长度为 1 帧。第二个线程将从输入队列中读取循环帧,如下所示:

    void FpsConverter::ThreadProc()
    {
    
    timeBeginPeriod(1);
    DWORD start_time = timeGetTime();
    int frame_counter = 0;
    while(!shouldFinish()) {
        Frame *frame = NULL;
        DWORD time_begin = timeGetTime();
        ReadInputFrame(frame);
        WriteToOutputQueue(frame);
        DWORD time_end = timeGetTime();
        DWORD next_frame_time = start_time + ++frame_counter * frame_time;
        DWORD time_to_sleep = next_frame_time - time_end;
        if (time_to_sleep > 0) {
            Sleep(time_to_sleep);
        }
    }
    timeEndPeriod(1);
    }
    

    当 CPU 能力足够且需要更高的保真度和平滑度时,您可以通过某种插值(类似于 mpeg 编解码器中使用的技术)计算输出帧,而不仅仅是从一帧,而是从更多帧。输出帧时间戳与输入帧时间戳越接近,您应该分配给这个特定输入帧的权重就越大。

    【讨论】:

      猜你喜欢
      • 2012-11-20
      • 2012-11-12
      • 2012-11-15
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      • 2013-10-27
      • 2013-01-16
      • 1970-01-01
      相关资源
      最近更新 更多