【问题标题】:ffmpeg Bmp to yuv : Crash at sws_scaleffmpeg Bmp 到 yuv:在 sws_scale 崩溃
【发布时间】:2015-12-29 04:01:15
【问题描述】:

上下文: 我有一系列连续的位图,我想将它们编码成一种轻量级的视频格式。 我在 qt5qt IDEmsvc2013 下使用 ffmpeg 版本 2.8.3(构建 here) > 对于 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 的建议,但现在我收到 bad src image pointers 错误,我已经调查并工作了很多小时,但没有找到解决方案。在这里,有新的 sn-p :

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

【问题讨论】:

  • IDK 为什么您的代码会崩溃,但为什么要使用固定比特率而不是 CRF,以及为什么 GOP 尺寸如此之小?从命令行,-preset slow -crf 20 应该提供良好的质量。 IDK 从哪里开始调试,因为您没有发布回溯显示函数参数对于最终出现段错误的函数是什么,更不用说调用树了。
  • @PeterCordes :这不是我的最终代码,这只是我放置主线的初稿,之后我将重新平衡。我只是想看看 ffmpeg mecanic 是否有效。然而,它在 swscale() 处崩溃。我无法使用这个 ffmpeg 构建探索 ffmpeg 代码(在 ffmpeg 网站上只有发布库构建),我只能看到带有 ffmpeg 函数的堆栈崩溃并且我的问题......触发了一个异常。堆栈为:swscale() -> RtlValidateHeap() (ntdll File) ->sws_get_class() (swscale_3 File)-> sws_getCachedContext() and Crash

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


【解决方案1】:

你正在使用avpicture_fill(),它是这样实现的:

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

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

此外,在您的测试代码中,您使用new(而不是av_malloc)来分配内存,并且从new 返回的指针也不能保证按32 字节对齐。

【讨论】:

  • sws_scale 如果我添加:avcodec_align_dimensions2 (c, &amp;c-&gt;width, &amp;c-&gt;height, inpic-&gt;linesize); 并且我将new 更改为av_malloc 转换为uint8_t。现在函数avcodec_encode_video2 (c, (AVPacket *) outbuf,(AVFrame *) outbuf_size, (int *) outpic); 在 sws_scale 触发异常之后调用。我要去检查outbuf,我觉得分配得不好。
  • 现在它“工作”了我忘记在swscale() 调用之后添加avcodec_align_dimensions2 (c, &amp;c-&gt;width, &amp;c-&gt;height, outpic-&gt;linesize);。尽管如此,我在崩溃前循环了 8 次,输出为Encoding frame X (size= -542398533 )。在堆栈上总是sws_get_CachedContext () 触发了异常。 (否则,我认为outbuf 分配得当)。
  • 我会通过 valgrind 或地址清理程序运行它...您还需要发布更新的代码。
  • 我刚刚编辑了我的帖子并根据您的建议更新了一个 sn-p。我收到来自sws_scale () 的错误bad dst image pointers
  • 啊,我明白了,你的操作顺序颠倒了。不要对目标图片执行 alloc+fill+align,而是执行 align+alloc(按此顺序)。同样,对于源,除非 FFmpeg 分配了数据,否则不要使用对齐。如果是这样,请使用对齐+填充。否则,使用原始数据的线条大小填充。如果它本身没有与 32 像素对齐,则需要在将 tmp 作为源交给 sws_scale 之前填充源并在两者之间对齐+填充临时图像和 av_picture_copy/av_image_copy。该错误是因为fill在av_picture_alloc之后将dst中的数据指针重置为NULL。
猜你喜欢
  • 1970-01-01
  • 2018-02-19
  • 2021-05-28
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多