views:

299

answers:

3

I have an Objective-C class (although I don't believe this is anything Obj-C specific) that I am using to write a video out to disk from a series of CGImages. (The code I am using at the top to get the pixel data comes right from Apple: http://developer.apple.com/mac/library/qa/qa2007/qa1509.html). I successfully create the codec and context - everything is going fine until it gets to avcodec_encode_video, when I get EXC_BAD_ACCESS. I think this should be a simple fix, but I just can't figure out where I am going wrong.

I took out some error checking for succinctness. 'c' is an AVCodecContext*, which is created successfully.

-(void)addFrame:(CGImageRef)img
{
  CFDataRef bitmapData = CGDataProviderCopyData(CGImageGetDataProvider(img));
  long dataLength = CFDataGetLength(bitmapData);
  uint8_t* picture_buff = (uint8_t*)malloc(dataLength);
  CFDataGetBytes(bitmapData, CFRangeMake(0, dataLength), picture_buff);

  AVFrame *picture = avcodec_alloc_frame();
  avpicture_fill((AVPicture*)picture, picture_buff, c->pix_fmt, c->width, c->height);

  int outbuf_size = avpicture_get_size(c->pix_fmt, c->width, c->height);
  uint8_t *outbuf = (uint8_t*)av_malloc(outbuf_size);

  out_size = avcodec_encode_video(c, outbuf, outbuf_size, picture); // ERROR occurs here
  printf("encoding frame %3d (size=%5d)\n", i, out_size);
  fwrite(outbuf, 1, out_size, f);

  CFRelease(bitmapData);
  free(picture_buff);
  free(outbuf);
  av_free(picture);
  i++;
}

I have stepped through it dozens of times. Here are some numbers...

  1. dataLength = 408960
  2. picture_buff = 0x5c85000
  3. picture->data[0] = 0x5c85000 -- which I take to mean that avpicture_fill worked...
  4. outbuf_size = 408960

and then I get EXC_BAD_ACCESS at avcodec_encode_video. Not sure if it's relevant, but most of this code comes from api-example.c. I am using XCode, compiling for armv6/armv7 on Snow Leopard.

Thanks so much in advance for help!

A: 

I have not enough information here to point to the exact error, but I think that the problem is that the input picture contains less data than avcodec_encode_video() expects:

avpicture_fill() only sets some pointers and numeric values in the AVFrame structure. It does not copy anything, and does not check whether the buffer is large enough (and it cannot, since the buffer size is not passed to it). It does something like this (copied from ffmpeg source):

    size = picture->linesize[0] * height;
    picture->data[0] = ptr;
    picture->data[1] = picture->data[0] + size;
    picture->data[2] = picture->data[1] + size2;
    picture->data[3] = picture->data[1] + size2 + size2;

Note that the width and height is passed from the variable "c" (the AVCodecContext, I assume), so it may be larger than the actual size of the input frame.

It is also possible that the width/height is good, but the pixel format of the input frame is different from what is passed to avpicture_fill(). (note that the pixel format also comes from the AVCodecContext, which may differ from the input). For example, if c->pix_fmt is RGBA and the input buffer is in YUV420 format (or, more likely for iPhone, a biplanar YCbCr), then the size of the input buffer is width*height*1.5, but avpicture_fill() expects the size of width*height*4.

So checking the input/output geometry and pixel formats should lead you to the cause of the error. If it does not help, I suggest that you should try to compile for i386 first. It is tricky to compile FFMPEG for the iPhone properly.

gyim
Thank you very much for your answer!Just as a test, I made picture_buff 10x bigger, and I get the same error. The source that you copied is expecting a PIX_FMT_YUV420P (planar) image, which is, I assume, why it is setting the 4 different pointers - it is pointing to the beginning of each channel. I guess I should have mentioned that, in my case, c->pix_fmt = PIX_FMT_RGB24I also do a check:if(CGImageGetWidth(img)!=c->width || CGImageGetHeight(img)!=c->height) { NSLog(@"Frame doesn't match movie size. Skipping"); return;}What other information can I provide to help you?
Jeff Crouse
Or -- to help me, I should say :)
Jeff Crouse
Well, then I think the bug is not here, but in the codec initialization code - which is much harder to debug :( What is most likely is that you did not set some parameters in AVCodecContext. You should check the comments in avcodec.h to see what's missing. If it does not help, there's nothing left than debugging the ffmpeg code... (it actually helps if you made a trivial error!)Does it crash right in avcodec_encode_video()? Because in that function I see only one thing to crash: if codecContext->codec is NULL (did you call av_register_all()?)
gyim
Here is what I don't understand. avcodec_encode_video is supposed to put the encoded version of 'picture' into 'outbuf', correct? 'picture' is 408960 bytes (having been given picture_buff, which is malloc'd with dataLength), and outbuf is 408960 bytes (having been malloc'd with outbuf_size), so where could the bad access possibly be happening? What part of memory am I neglecting here?
Jeff Crouse
I'm pretty sure you are right. I am now passing the codec and the context straight from the class I am using to decode the video and it seems to be working. I have other problems elsewhere, but I will post here when I figure it out.
Jeff Crouse
A: 

Does the codec you are encoding support the RGB color space? You may need to use libswscale to convert to I420 before encoding. What codec are you using? Can you post the code where you initialize your codec context?

ryan
H.264 does support RGB. Mpeg4 doesn't. I have tried both. Right now I am taking the context and codec directly from the code that opens the video. Is there any reason this shouldn't work?Reader: http://github.com/jefftimesten/Unlogo/blob/master/Classes/VideoFrameExtractor.mWriter: http://github.com/jefftimesten/Unlogo/blob/master/Classes/VideoMaker.m
Jeff Crouse
A: 

The function RGBtoYUV420P may help you. http://www.mail-archive.com/[email protected]/msg03956.html

SUKIYAKI