3

我在使用 libav 库的视频聊天应用程序中遇到了一些问题。我正在通过 UDP 将 VP8 编码的 1080p 视频作为 WebM 容器发送,并且效果很好。大多数时候,任何一方的解码器都会从传输造成的数据包丢失中恢复过来。

然而,在某个时间点,它只是冻结并且永远不会再次恢复。这最终发生在双方。当通过有损传输通道发送时,我正在寻找 VP8 编解码器参数以设置以提高鲁棒性。我结合了一些我发现的东西,以增加稳健性。但是,经过一段时间的视频聊天后,它仍然会冻结。

这是我目前使用的参数。

  pVidCodecCtx->codec_id     = AV_CODEC_ID_VP8;
  pVidCodecCtx->codec_type   = AVMEDIA_TYPE_VIDEO;
  pVidCodecCtx->width        = frmQ->pCodecCtx->width; //1920
  pVidCodecCtx->height       = frmQ->pCodecCtx->height; //1080
  pVidCodecCtx->time_base    = frmQ->pCodecCtx->time_base;
  pVidCodecCtx->pix_fmt      = PIX_FMT_YUV420P;
  pVidCodecCtx->qmin         = 4;
  pVidCodecCtx->qmax         = 56;
  pVidCodecCtx->bit_rate     = pVidCodecCtx->width * pVidCodecCtx->height * 6;
  pVidCodecCtx->slices       = 8;
  pVidCodecCtx->profile      = 3;
  pVidCodecCtx->thread_count = 3;
  pVidCodecCtx->keyint_min   = 5;
  av_dict_set(&pDictCodecOpts, "rc_lookahead", "0", 0);
  av_dict_set(&pDictCodecOpts, "quality", "realtime", 0);
  av_dict_set(&pDictCodecOpts, "deadline", "realtime", 0);
  av_dict_set(&pDictCodecOpts, "max-intra-rate", "0", 0);
  av_dict_set(&pDictCodecOpts, "qcomp", "0", 0);
  av_dict_set(&pDictCodecOpts, "default", "er", 0);
  av_dict_set(&pDictCodecOpts, "error_resilient", "er", 0);
  av_dict_set(&pDictCodecOpts, "partitions", "er", 0);

我从vpx 编码器的 ffmpeg 代码中提取的大部分参数。

我是否还必须为解码器设置参数以增加错误恢复能力?还是我在编码器中遗漏了一些参数或设置不正确。非常感谢任何帮助或提示。

4

1 回答 1

3

我会回答我自己的问题,因为我最终设法让视频聊天运行而不会冻结。

事实证明,这些参数实际上有利于错误恢复。

问题是一些函数调用,例如

av_read_frame()
avformat_open_input()

当包损坏时,解码器线程中的线程被阻塞,从而导致视频冻结。

所以我最终做的是编写一个计时器类,让计时器测量所述功能的执行时间。我写了一个中断回调函数并将它传递给我的解码器的格式上下文,如下所示:

static int interrupt_cb (void *p)
{
    unsigned int expTime = 1000;
    Uint32 elapsedTime = pVidConfTimer.elapsedTimeInMs();
    if (elapsedTime > expTime) 
    {
       return 1;
    }
    return 0;
}

static const AVIOInterruptCB cb = {interrupt_cb, &dummy};
frmQ.pFormatCtx->interrupt_callback = cb;

如果执行时间比 expTime 长,这将从函数返回。您还可以将自定义参数传递void *p给回调函数

在我的解码/显示线程中,我只是调用类似

timer.tic();
ret = av_read_frame(...);
timer.reset();

if (ret<0)
{
    //received corrupted frame
    //reinitialize format context
    //open input
    //find decoder and open codec
    ...
}

希望这可以帮助任何有类似问题的人。

于 2014-10-24T09:17:12.123 回答