c++ - ffmpeg Bmp 到 yuv : Crash at sws_scale

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

上下文: 我有一系列连续的位图,我想将它们编码成一种轻型视频格式。 我使用 ffmpeg 版本 2.8.3(内部版本 here),在 qt5qt IDEmsvc2013 适用于 win32

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

LIBS += -lavcodec -lavformat -lswscale -lavutil

swscale 是哪个 bug。这是代码:

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() 处崩溃,调用 _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);

最佳答案

您正在使用 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 的最后一个参数(), 对齐=1。这意味着缓冲区行将未对齐。不幸的是,这在文档中一点也不清楚,大多数 FFmpeg 函数需要缓冲线对齐到允许 SSE2 或 AVX2 优化的二次幂,例如对齐=32。请参阅 this response 中的第二个要点关于如何以编程方式执行此操作。

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

关于c++ - ffmpeg Bmp 到 yuv : Crash at sws_scale,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34450151/

相关文章:

c++ - 如何在Mac OS X上安装ruby-filemagic?

c++ - 指向数组访问的指针出现段错误

c++ - Arduino 库 : multiple definitions of a function

android - FFMPEG:初始化复杂过滤器时出错。无效的论点

ffmpeg - ffplay可以看字幕吗

c++ - 对从文件读取到结构中的结构感到困惑

visual-studio - '/fp :fast' and '/Za' not compatible Visual C++

c - IMAGE_REL_AMD64_ADDR64 64 位重定位

c - 如何使用 VC++ 编译器刷新 .C 程序中的标准输入设备?

java - 如何使用 FFmpeg 调整 yuv420sp 的大小