5

AVFrame をパケットにエンコードしようとしているときに問題が発生しました。

コード全体を読む前に、入力が機能していることをテストしました。出力内容は、こちらの例からのものです。問題があると思います。しかし、ループの終わり近くでセグメンテーション違反が発生します。

ここに私の削減されたコードがあります:

void nmain() {

  // input stuff
  AVFormatContext *formatCtxIn=0;
  AVInputFormat *formatIn=0;
  AVCodecContext *codecCtxIn=0;
  AVCodec *codecIn;
  AVPacket *pktIn;

  av_register_all();
  avdevice_register_all();
  avcodec_register_all();


  formatIn = av_find_input_format("dshow");

  if(!formatIn)
    return;


  AVDictionary *avoption=0;
  av_dict_set(&avoption, "rtbufsize", "1000000000", NULL);

  if(avformat_open_input(&formatCtxIn, "video=Integrated Camera", formatIn, &avoption)!=0)
    return;

  if(avformat_find_stream_info(formatCtxIn, NULL)<0)
    return;

  codecCtxIn = formatCtxIn->streams[0]->codec;
  codecIn = avcodec_find_decoder(codecCtxIn->codec_id);

  if(avcodec_open2(codecCtxIn, codecIn, NULL)<0)
    return;


  // end input stuff  
//------------------------------------------------------------------------------
  // output stuff

  AVOutputFormat *formatOut=0;
  AVFormatContext *formatCtxOut=0;
  AVStream *streamOut=0;
  AVFrame *frame=0;
  AVCodec *codecOut=0;
  AVPacket *pktOut;

  const char *filename = "test.mpeg";

  formatOut = av_guess_format(NULL, filename, NULL);
  if(!formatOut)
    formatOut = av_guess_format("mpeg", NULL, NULL);
  if(!formatOut)
    return;

  formatCtxOut = avformat_alloc_context();
  if(!formatCtxOut)
    return;

  formatCtxOut->oformat = formatOut;

  sprintf(formatCtxOut->filename, "%s", filename);

  if(formatOut->video_codec != AV_CODEC_ID_NONE) {
    AVCodecContext *ctx;

    codecOut = avcodec_find_encoder(formatOut->video_codec);
    if(!codecOut)
      return;

    streamOut = avformat_new_stream(formatCtxOut, codecOut);
    if(!streamOut)
      return;

    ctx = streamOut->codec;

    ctx->bit_rate = 400000;
    ctx->width    = 352;
    ctx->height   = 288;
    ctx->time_base.den = 25;
    ctx->time_base.num = 1;
    ctx->gop_size      = 12;
    ctx->pix_fmt       = AV_PIX_FMT_YUV420P;

    if(ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
      ctx->max_b_frames = 2;
    if(ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
      ctx->mb_decision = 2;


    if(formatCtxOut->oformat->flags & AVFMT_GLOBALHEADER)
      ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
  }

  if(streamOut) {
    AVCodecContext *ctx;
    ctx = streamOut->codec;

    if(avcodec_open2(ctx, codecOut, NULL) < 0)
      return;
  }

  if(!(formatCtxOut->flags & AVFMT_NOFILE))
    if(avio_open(&formatCtxOut->pb, filename, AVIO_FLAG_WRITE) < 0)
      return;

  avformat_write_header(formatCtxOut, NULL);


  // doit

  pktIn = new AVPacket;
  pktOut = new AVPacket;
  av_init_packet(pktOut);
  pktOut->data = 0;

  frame = avcodec_alloc_frame();
  if(!frame)
    return;

  for(;;) {
    if(av_read_frame(formatCtxIn, pktIn) >= 0) {
      av_dup_packet(pktIn);

      int fff;
      if(avcodec_decode_video2(codecCtxIn, frame, &fff, pktIn) < 0)
        std::cout << "bad frame" << std::endl;

      if(!fff)
        return;  // ok

      static int counter=0;
      SaveFrame(frame, codecCtxIn->width, codecCtxIn->height, counter++);  // work fine

      // here a segmentation fault is occured.
      if(avcodec_encode_video2(streamOut->codec, pktOut, frame, &fff) < 0)
        std::cout << "bad frame" << std::endl;
    }
  }
}


// only for testing
// add to ensure frame is valid
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
  FILE *pFile;
  char szFilename[32];
  int y;

  // Open file
  sprintf(szFilename, "frame%d.ppm", iFrame);
  pFile=fopen(szFilename, "wb");
  if(pFile==NULL)
      return;

  // Write header
  fprintf(pFile, "P6\n%d %d\n255\n", width, height);

  // Write pixel data
  for(y=0; y<height; y++)
      fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

  // Close file
  fclose(pFile);
}

私は何を間違っていますか?

デバッグ中に、パラメーターに問題は見つかりませんでした。streamOut->codec満たされています。pktOut割り当てられ、frame以前にエンコードされた画像で満たされます。問題は出力コーデックの作成中にあると思いますが、例を見てdoxypagesを見ると正しいようです。

テスト

トレース ルートは、msvc11 とフレームワーク 5 を使用した QT からのものです。

私も博士と一緒に走ろうとしました。メモリとこれを取得します:

Error #26: UNADDRESSABLE ACCESS: reading 0x00000000-0x00000004 4 byte(s)
# 0 replace_memcpy                      [d:\derek\drmemory\withwiki\trunk\drmemory\replace.c:203]
# 1 avcodec-54.dll!ff_dct_quantize_c   +0xd463   (0x6a482364 <avcodec-54.dll+0x3c2364>)
# 2 avcodec-54.dll!avcodec_encode_video2+0xb7     (0x6a56a5b8 <avcodec-54.dll+0x4aa5b8>)
# 3 nmain                               [d:\prg\tests\recording system-qt\libav\recsys\main.cpp:610]
# 4 main                                [d:\prg\tests\recording system-qt\libav\recsys\main.cpp:182]
Note: @0:00:06.318 in thread 5312
Note: instruction: mov    (%edx) -> %ebx

memcpyがうまくいかないときの読み込み処理のようです。


バージョン:

私が使用している libav/ffmpeg のバージョンについて言及するのを忘れていました:

libavutil      51. 76.100 / 51. 76.100
libavcodec     54. 67.100 / 54. 67.100
libavformat    54. 33.100 / 54. 33.100
libavdevice    54.  3.100 / 54.  3.100
libavfilter     3. 19.103 /  3. 19.103
libswscale      2.  1.101 /  2.  1.101
libswresample   0. 16.100 /  0. 16.100
libpostproc    52.  1.100 / 52.  1.100

補遺:

関数はチュートリアル 1SafeFrameからコピーされます。

4

2 に答える 2

0

そのトランスコーディング ループには、少なくとも 2 つの問題があります。

1)デコーダーがフレームを生成したかどうかを確認していません。多くのデコーダーには入力と出力の間に遅延があるため、エラーが発生しなくても、デコード呼び出しで必ずしもフレームが生成されるとは限りません。フレームを返し始めるまでパケットをデコーダーに渡し続ける必要があります (ドキュメントで説明されているように、最後に NULL パケットでフラッシュします)。

その結果、初期化されていないフレームがエンコーダーに渡されます。これがおそらくクラッシュの原因です。

2) 私が目にするもう 1 つの問題は、出力パケットを 1 回だけ開始していることです。ドキュメントが言うように

ユーザーは、関数を呼び出す前に avpkt->data および avpkt->size を設定することによって出力バッファーを提供できますが、ユーザー提供のデータのサイズが十分に大きくない場合、エンコードは失敗します。他のすべての AVPacket フィールドは、av_init_packet() を使用してエンコーダーによってリセットされます。avpkt->data が NULL の場合、エンコーダーが割り当てます。エンコーダーは avpkt->size を出力パケットのサイズに設定します。返されたデータ (存在する場合) は呼び出し元に属し、呼び出し元はそれを解放する責任があります。

したがって、トランスコード ループを開始する前に 1 回だけ初期化すると、最初の反復以降の各反復で古いデータが含まれます。エンコーダーは、そのバッファーをエンコードに使用して上書きしたいと考えます。そのパケットをすでにマルチプレクサなどに渡している場合、これは涙で終わります。そのため、各エンコード呼び出しの前に、必ずパケット データとサイズを NULL/0 に初期化してください。

于 2013-06-07T19:58:04.147 に答える