avcodec_encode_video2时出现分段错误 [英] Segmentation fault while avcodec_encode_video2

查看:240
本文介绍了avcodec_encode_video2时出现分段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试将AVFrame编码为数据包时遇到一些问题。



在阅读整个代码之前,输入的东西已经正常工作,我已经对其进行了测试。输出的内容来自示例。

解决方案

最后,我解决了我的问题。



问题出在(除了libav的文档之外) )avpacket不是数据包中图片的(真实)副本。它只是指向数据包的数据。



所以首先我为输出创建了一个新的avframe,并在其上创建了一个缓冲区avframe

  AVFrame * outpic = avcodec_alloc_frame(); 
nbytes = avpicture_get_size(codecCtxOut-> pix_fmt,codecCtxOut->宽度,codecCtxOut->高度);
uint8_t *外缓冲区=(uint8_t *)av_malloc(nbytes);

此缓冲区用于从输入到输出的转换。然后在循环中,我必须用缓冲区填充主题(avframe)。我在代码中发现
,该函数正在用缓冲区填充平面指针。
请参见此处

  avpicture_fill((AVPicture *)outpic,outbuffer,AV_PIX_FMT_YUV420P,codecCtxOut->宽度,codecCtxOut->高度); 

然后我使用 sws_scale 。但是首先必须设置swscontext。

  SwsContext * swsCtx_ = sws_getContext(codecCtxIn-> width,codecCtxIn-> height ,
codecCtxIn-> pix_fmt,
codecCtxOut->宽度,codecCtxOut->高度,
codecCtxOut-> pix_fmt,
SWS_BICUBIC,NULL,NULL,NULL);

sws_scale(swsCtx_,inpic-> data,inpic-> linesize,0,codecCtxIn-> height,outpic-> data,outpic-> linesize);

然后,您可以将主题编码为pktout(用于输出的avpacket)。但是首先请释放输出数据包,否则您将得到一个错误和泄漏... 参见此处

  av_free_packet(pktOut); 

if(avcodec_encode_video2(streamOut->编解码器,pktOut,outpic和&fff)< 0){
std :: cout<< 屎框<< std :: endl;
继续;
}
//并将其写入文件
formatOut-> write_packet(formatCtxOut,pktOut);

所以现在对我来说(几乎可以使用)。仍然是一个小内存泄漏,但是我以后可以发现。


I have some problems while trying to encode a AVFrame to a packet.

Before reading the whole code, the input stuff is working, I tested it. The output stuff is from an example here. I think there is the problem. But the segmentation fault occurs in the loop near the end.

Here is my reduced code:

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);
}

What am i doing wrong?

While debugging i didn't found any problems with the parameters. streamOut->codec is filled. pktOut is allocated and frame is filled with the picture encoded before. I think the problem is while creating the output codec but watching the example and looking to the doxypages it seems to be correct.

The trace route is from QT using msvc11 and framework 5.

I also tried to run with dr. memory and get this:

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

It seems like the reading process while memcpy is going wrong.


Version:

I've forgot to mention the version of libav/ffmpeg i'm using:

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


Addendum:

Function SafeFrame is copied from tutorial 1.

解决方案

Finally i solved my problem.

The problem is (apart from the documentation of libav) avpacket is not a (real) copy of the picture in the packet. it just points to the data of the packet. You have to make a copy, or better you have to let it libav do.

So first i created a new avframe for the output and a buffer on which the output avframe is pointing to.

AVFrame *outpic = avcodec_alloc_frame();
nbytes = avpicture_get_size(codecCtxOut->pix_fmt, codecCtxOut->width, codecCtxOut->height);
uint8_t* outbuffer = (uint8_t*)av_malloc(nbytes);

This buffer is used for the conversion from input to output. Then in the loop i have to fillup the outpic (avframe) with the buffer. I have found in the code that this function is filling up the plane pointers with the buffer. see here

avpicture_fill((AVPicture*)outpic, outbuffer, AV_PIX_FMT_YUV420P, codecCtxOut->width, codecCtxOut->height);

Then i converted the inpic to outpic using sws_scale. But first you have to setup the swscontext.

SwsContext* swsCtx_ = sws_getContext(codecCtxIn->width, codecCtxIn->height,
                                     codecCtxIn->pix_fmt,
                                     codecCtxOut->width, codecCtxOut->height,
                                     codecCtxOut->pix_fmt,
                                     SWS_BICUBIC, NULL, NULL, NULL);

sws_scale(swsCtx_, inpic->data, inpic->linesize, 0, codecCtxIn->height, outpic->data, outpic->linesize);

Then you can encode the outpic into pktout (avpacket for output). But first do free the output packet, otherwise you will get an error and a leak... see here

av_free_packet(pktOut);

if(avcodec_encode_video2(streamOut->codec, pktOut, outpic, &fff) < 0) {
  std::cout << "shit frame" << std::endl;
  continue;
}
// and write it to the file
formatOut->write_packet(formatCtxOut, pktOut);

So now it works (nearly fine) for me. Still a small memory leak, but this i can spot later.

这篇关于avcodec_encode_video2时出现分段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆