ffmpeg Bmp to yuv:在sws_scale崩溃

时间:2015-12-24 09:10:04

标签: c++ visual-c++ ffmpeg x86 qt5

上下文: 我有一连串的连续位图,我想把它们编码成一个轻量级视频格式。 我在 qt5 qt IDE msvc2013 <下使用 ffmpeg 版本2.8.3(版本here) / em> for win32

问题: 我的代码在 sws_scale()(有时在 avcodec_encode_video2())崩溃。当我探索堆栈时,崩溃事件发生在 sws_getCachedContext()。 (我只能看到这些ffmpeg构建的堆栈)。 我只使用这些ffmpeg库(来自Qt .pro 文件):

LIBS += -lavcodec -lavformat -lswscale -lavutil

swscale 哪个错误。这就是代码:

void newVideo ()
{
    ULONG_PTR gdiplusToken;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    initBitmap (); //init bmp
    int screenWidth =  bmp.bmiHeader.biWidth;
    int screenHeight = bmp.bmiHeader.biHeight;

    AVCodec * codec;
    AVCodecContext * c = NULL;
    uint8_t * outbuf;
    int i, out_size, outbuf_size;


    avcodec_register_all();

    qDebug () << "Video encoding\n";

    // Find the mpeg1 video encoder
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        qDebug () << "Codec not found\n";
        avcodec_close(c);
        av_free(c);
        return;
    }
    else
        qDebug () << "H264 codec found\n";

    c = avcodec_alloc_context3(codec);

    c->bit_rate = 1000000;
    c->width = 800; // resolution must be a multiple of two (1280x720),(1900x1080),(720x480)
    c->height = 600;
    c->time_base.num = 1; // framerate numerator
    c->time_base.den = 25; // framerate denominator
    c->gop_size = 30; // emit one intra frame every ten frames
    c->max_b_frames = 1; // maximum number of b-frames between non b-frames
    c->pix_fmt = AV_PIX_FMT_YUV420P; //Converstion RGB to YUV ?
    c->codec_id = AV_CODEC_ID_H264;

    struct SwsContext* fooContext = sws_getContext(screenWidth, screenHeight,
                                                   AV_PIX_FMT_RGB32,
                                                   c->width, c->height,
                                                   AV_PIX_FMT_YUV420P,
                                                   SWS_FAST_BILINEAR,
                                                   NULL, NULL, NULL);

    // Open the encoder
    if (avcodec_open2(c, codec, NULL) < 0)
    {
        qDebug () << "Could not open codec\n";
        avcodec_close(c);
        av_free(c);
        return;
    }
    else qDebug () << "H264 codec opened\n";

    outbuf_size = 100000 + c->width*c->height*(32>>3);//*(32>>3); // alloc image and output buffer
    outbuf = static_cast<uint8_t *>(malloc(outbuf_size));
    qDebug() << "Setting buffer size to: " << outbuf_size << "\n";

    FILE* f = fopen("TEST.mpg","wb");
    if(!f) qDebug() << "x - Cannot open video file for writing\n";
    else qDebug() << "Opened video file for writing\n";

    // encode 5 seconds of video
    for (i = 0; i < STREAM_FRAME_RATE*STREAM_DURATION; i++) //the stop condition i < 5.0*5
    {
        qDebug () << "i = " << i;
        fflush(stdout);

        HBITMAP hBmp;
        if (GetScreen(hBmp) == -1) return;
        BYTE * pPixels;// = new BYTE [bmp.bmiHeader.biSizeImage];
        pPixels = getPixels (hBmp);
        DeleteObject (hBmp);

        int nbytes = avpicture_get_size(AV_PIX_FMT_YUV420P, c->width, c->height);
        uint8_t* outbuffer = (uint8_t*)av_malloc(nbytes*sizeof(uint8_t));
        if(!outbuffer) // check if(outbuf) instead
        {
            qDebug () << "Bytes cannot be allocated";
            return;
        }

        AVFrame* inpic = avcodec_alloc_frame(); //av_frame_alloc () ?
        AVFrame* outpic = avcodec_alloc_frame();

        outpic->pts = (int64_t)((float)i * (1000.0/((float)(c->time_base.den))) * 90);
        if (avpicture_fill((AVPicture*) inpic, (uint8_t*) pPixels, AV_PIX_FMT_RGB32,
                       screenWidth, screenHeight) < 0)
            qDebug () <<  "avpicture_fill Fill picture with image failed"; //Fill picture with image

        if(avpicture_fill((AVPicture*) outpic, outbuffer, AV_PIX_FMT_YUV420P,
                       c->width, c->height) < 0)
            qDebug () <<  "avpicture_fill failed";

        if (av_image_alloc(outpic->data, outpic->linesize, c->width, c->height,
                       c->pix_fmt, 1) < 0)
            qDebug () <<  "av_image_alloc failed";

        inpic->data[0] += inpic->linesize[0]*(screenHeight - 1); // Flipping frame
        inpic->linesize[0] = -inpic->linesize[0]; // Flipping frame

////////////////////////////HERE THE BUG////////////////////////////////
        sws_scale(fooContext,
                  inpic->data, inpic->linesize,
                  0, c->height,
                  outpic->data, outpic->linesize); //HERE THE BUG

        av_free_packet((AVPacket *)outbuf);
        // encode the image
        out_size = avcodec_encode_video2 (c, (AVPacket *) outbuf,
                                          (AVFrame *) outbuf_size, (int *) outpic);
///////////////////////THE CODE DONT GO BEYOND/////////////////////////////////

        qDebug () << "Encoding frame" << i <<" (size=" << out_size <<"\n";
        fwrite(outbuf, 1, out_size, f);
        delete [] pPixels;
        av_free(outbuffer);
        av_free(inpic);
        av_freep(outpic);
    }

    // get the delayed frames
    for(; out_size; i++)
    {
        fflush(stdout);
        out_size = avcodec_encode_video2 (c, (AVPacket *) outbuf,
                                          (AVFrame *) outbuf_size, NULL);
        qDebug () << "Writing frame" << i <<" (size=" << out_size <<"\n";
        fwrite(outbuf, 1, out_size, f);
    }

    // add sequence end code to have a real mpeg file
    outbuf[0] = 0x00;
    outbuf[1] = 0x00;
    outbuf[2] = 0x01;
    outbuf[3] = 0xb7;
    fwrite(outbuf, 1, 4, f);
    fclose(f);

    avcodec_close(c);
    free(outbuf);
    av_free(c);
    qDebug () << "Closed codec and Freed\n";
}

输出:

Video encoding

H264 codec found

H264 codec opened

Setting buffer size to:  2020000 

Opened video file for writing

i =  0
**CRASH**

我知道我的位图并不好,所以我为了测试而制作了一个位图,代码是:

    uint8_t* pPixels = new uint8_t[Width * 3 * Height];
    int x = 50;
    for(unsigned int i = 0; i < Width * 3 * Height; i = i + 3) // loop for generating color changing images
    {
        pPixels [i] = x % 255; //R
        pPixels [i + 1] = (x) % 255; //G
        pPixels [i + 2] = (255 - x) % 255; //B
    }

但崩溃继续。也许,它可能证明它不是有问题的位图(pPixels)。

如果有人知道,为什么我会遇到这个错误:也许我没有设置好一个参数?或者一个ffmpeg已弃用功能?等

编辑1 27/12/15

感谢 Ronald S. Bultje 函数 sws_scale()不会因此代码而崩溃,然而我收到错误< em>坏dst图像指针。我的代码:

//DESTINATION FRAME            
if (avpicture_alloc ((AVPicture*) dst_frame, AV_PIX_FMT_YUV420P, c->width, c->height) < 0)
            {
                qDebug () <<  "# avpicture_alloc failed";
                return;
            }
            if(avpicture_fill((AVPicture*) dst_frame, NULL, AV_PIX_FMT_YUV420P,
                           c->width, c->height) < 0)
                qDebug () <<  "avpicture_fill failed";
            avcodec_align_dimensions2 (c, &c->width, &c->height, dst_frame->linesize);

//SOURCE FRAME
            if (avpicture_fill((AVPicture*) src_frame, (uint8_t *) pPixels, AV_PIX_FMT_RGB32,
                               tmp_screenWidth, tmp_screenHeight) < 0)
                qDebug () <<  "# avpicture_fill Fill picture with image failed"; //Fill picture with image
            avcodec_align_dimensions2 (c, &tmp_screenWidth, &tmp_screenHeight, src_frame->linesize);

            struct SwsContext* conversionContext = sws_getContext(tmp_screenWidth,tmp_screenHeight,AV_PIX_FMT_RGB32,c->width, c->height, AV_PIX_FMT_YUV420P,SWS_FAST_BILINEAR, NULL, NULL, NULL);

            int output_Height = sws_scale(conversionContext,
                                          src_frame->data, src_frame->linesize,
                                          0, tmp_screenHeight,
                                          dst_frame->data, dst_frame->linesize); //return 0 -> bad dst image pointers error

编辑2 28/12/15

我试图遵循Ronald S. Bultje的建议,现在我得到一个坏的src图像指针错误,我已经调查并工作了很多个小时但我没有找到解决方案。这里有一个新的片段:

AVFrame* src_frame = av_frame_alloc ();
AVFrame* dst_frame = av_frame_alloc ();
AVFrame* tmp_src_frame = av_frame_alloc ();

/*........I do not use them until this snippet..........*/
//DESTINATION
//avpicture_free ((AVPicture*)dst_frame);
avcodec_align_dimensions2 (c, &c->width, &c->height, dst_frame->linesize);
if (avpicture_alloc ((AVPicture*) dst_frame, AV_PIX_FMT_YUV420P, c->width, c->height) < 0)
{
    qDebug () <<  "# avpicture_alloc failed";
    return;
}

//SOURCE
//stride = src_frame->linesize [0] = ((((screenWidth * bitPerPixel) + 31) & ~31) >> 3); do I need to do that ?
//== stride - I have gotten this formula from : https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx
if (avpicture_fill((AVPicture*) src_frame, (uint8_t *) pPixels, AV_PIX_FMT_RGB32,
                   screenWidth, screenHeight) < 0)
    qDebug () <<  "# avpicture_fill Fill picture with image failed"; //Fill picture with image
//linesize [0] == 21760 like commented stride

//Source TO TMP Source
avcodec_align_dimensions2 (c, &tmp_screenWidth, &tmp_screenHeight, tmp_src_frame->linesize);
if (avpicture_fill((AVPicture*) tmp_src_frame, NULL, AV_PIX_FMT_RGB32,
                   tmp_screenWidth, tmp_screenHeight) < 0)
    qDebug () <<  "# avpicture_fill Fill picture with image failed"; //Fill picture with image

av_picture_copy ((AVPicture*) tmp_src_frame, (const AVPicture*) src_frame, AV_PIX_FMT_RGB32,
                 screenWidth, screenHeight);

struct SwsContext* conversionContext = sws_getContext(tmp_screenWidth, tmp_screenHeight,
                                                      AV_PIX_FMT_RGB32,
                                                      c->width, c->height,
                                                      AV_PIX_FMT_YUV420P,
                                                      SWS_FAST_BILINEAR,
                                                      NULL, NULL, NULL);

int output_Height = sws_scale(conversionContext,
                              tmp_src_frame->data, tmp_src_frame->linesize,
                              0, tmp_screenHeight,
                              dst_frame->data, dst_frame->linesize);
//ffmpeg error = bad src image pointers
// output_Height == 0

编辑3

对于临时图片我已经完成avcode_align_dimension2()然后avpicture_alloc()分配内存和avpicture_fill()以填充图片指针。更新后的代码下方:

//DESTINATION
//avpicture_free ((AVPicture*)dst_frame);
avcodec_align_dimensions2 (c, &c->width, &c->height, dst_frame->linesize);
if (avpicture_alloc ((AVPicture*) dst_frame, AV_PIX_FMT_YUV420P, c->width, c->height) < 0)
{
    qDebug () <<  "# avpicture_alloc failed";
    return;
}

//SOURCE
//src_frame->linesize [0] = ((((screenWidth * bpp) + 31) & ~31) >> 3);
//src_frame->linesize [0] = stride;
if (avpicture_fill((AVPicture*) src_frame, (uint8_t *) pPixels, AV_PIX_FMT_RGB32,
                   screenWidth, screenHeight) < 0)
    qDebug () <<  "# avpicture_fill Fill picture with image failed"; //Fill picture with image

//Source TO TMP Source
avcodec_align_dimensions2 (c, &tmp_screenWidth, &tmp_screenHeight, tmp_src_frame->linesize);
if (avpicture_alloc ((AVPicture*) tmp_src_frame, AV_PIX_FMT_RGB32, tmp_screenWidth, tmp_screenHeight) < 0)
{
    qDebug () <<  "# avpicture_alloc failed";
    return;
}
int outbuf_size = tmp_screenWidth*tmp_screenHeight*4;// alloc image and output buffer
outbuf = static_cast<uint8_t *>(malloc(outbuf_size));
if (avpicture_fill((AVPicture*) tmp_src_frame, outbuf, AV_PIX_FMT_RGB32,
                   tmp_screenWidth, tmp_screenHeight) < 0)
    qDebug () <<  "# avpicture_fill Fill picture with image failed"; //Fill picture with image
av_picture_copy ((AVPicture*) tmp_src_frame, (const AVPicture*) src_frame, AV_PIX_FMT_RGB32,
                 tmp_screenWidth, tmp_screenHeight);

struct SwsContext* conversionContext = sws_getContext(tmp_screenWidth, tmp_screenHeight,
                                                      AV_PIX_FMT_RGB32,
                                                      c->width, c->height,
                                                      AV_PIX_FMT_YUV420P,
                                                      SWS_FAST_BILINEAR,
                                                      NULL, NULL, NULL);

int output_Height = sws_scale(conversionContext,
                              tmp_src_frame->data, tmp_src_frame->linesize,
                              0, tmp_screenHeight,
                              dst_frame->data, dst_frame->linesize);

调用堆栈如下:av_picture_copy()然后调用av_image_copy()然后_VEC_memcpy()然后调用fastcopy_I()并崩溃...问题不是尺寸(tmp_screenWidth / Height) )? (使用av_picture_copy ()我们可以将带有暗淡W1xH1的图片P1复制到尺寸为W2xH2的图片P2吗?)

编辑4

av_picture_copy() call _aligned_malloc()然后av_image_copy _VEC_memcpy()fastcopy_I()

//SOURCE if (avpicture_fill((AVPicture*) src_frame, (uint8_t *) pPixels, AV_PIX_FMT_RGB32, screenWidth, screenHeight) < 0) qDebug () << "# avpicture_fill Fill picture with image failed"; //Fill picture with image //Source TO TMP Source avcodec_align_dimensions2 (c, &tmp_screenWidth, &tmp_screenHeight, tmp_src_frame->linesize); if (avpicture_alloc ((AVPicture*) tmp_src_frame, AV_PIX_FMT_RGB32, tmp_screenWidth, tmp_screenHeight) < 0) { qDebug () << "# avpicture_alloc failed"; return; } av_picture_copy ((AVPicture*) tmp_src_frame, (const AVPicture*) src_frame, AV_PIX_FMT_RGB32, tmp_screenWidth, tmp_screenHeight); 崩溃
doc 2007

1 个答案:

答案 0 :(得分:4)

您正在使用avpicture_fill(),其实现方式如下:

IFERROR

注意av_image_fill_arrays()的最后一个参数,align = 1。这意味着缓冲行将未对齐。不幸的是,这在文档中根本不清楚,大多数FFmpeg函数要求缓冲线与2的幂对齐,允许SSE2或AVX2优化,例如,对齐= 32。请参阅this response中有关如何以编程方式执行此操作的第二个要点。

除此之外,在您的测试代码中,您使用No(而不是int avpicture_fill(AVPicture *picture, const uint8_t *ptr, enum AVPixelFormat pix_fmt, int width, int height) { return av_image_fill_arrays(picture->data, picture->linesize, ptr, pix_fmt, width, height, 1); } )来分配内存,而new返回的指针也不能保证对齐32字节。