5

v4l 経由でカメラからビデオ フレームを取得しています。RTP 経由で連続してストリーミングするには、それらを mpeg4 形式でトランスコードする必要があります。

すべてが実際に「機能」しますが、再エンコード中にはできないことがあります。入力ストリームは 15 fps を生成し、出力は 25 fps であり、すべての入力フレームは 1 つのビデオ オブジェクト シーケンスに変換されます (簡単なチェックでこれを確認しました)。出力ビットストリームで)。受信者は mpeg4 ビットストリームを正しく解析していると思いますが、RTP パケット化は何らかの形で間違っています。エンコードされたビットストリームを 1 つ以上の 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 );
    }
  }
}
4

1 に答える 1

0

最も簡単な解決策は、2 つのスレッドを使用することです。最初のスレッドは、質問で概説されているすべてのこと(デコード、スケーリング/色空間変換、コーディング)を行います。部分的にトランスコードされたフレームは、2 番目のスレッドと共有される中間キューに書き込まれます。このキューの最大長は、この特定のケース (低いビットレートから高いビットレートへの変換) では 1 フレームになります。2 番目のスレッドは、次のように入力キューからループ フレームを読み取ります。

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 パワーが十分で、より高い忠実度と滑らかさが必要な場合は、出力フレームを 1 つのフレームだけでなく、ある種の補間 (mpeg コーデックで使用される手法と同様) によって複数のフレームから計算できます。出力フレームのタイム スタンプが入力フレームのタイム スタンプに近いほど、この特定の入力フレームにより多くの重みを割り当てる必要があります。

于 2011-07-28T20:06:54.870 に答える