【问题标题】:Segmentation fault with avcodec_encode_video2() while encoding H.264编码 H.264 时 avcodec_encode_video2() 的分段错误
【发布时间】:2015-10-01 22:27:30
【问题描述】:

我正在尝试将 cv::Mat 转换为 AVFrame 以在 H.264 中对其进行编码,并想从一个简单的示例开始,因为我在这两个方面都是新手。所以我先读入一个JPEG文件,然后用sws_scale()AV_PIX_FMT_BGR24AV_PIX_FMT_YUV420P进行像素格式转换,保持尺寸不变,直到我打电话给avcodec_encode_video2(),一切都很好。

我阅读了很多关于AVFrame 分配的讨论,Segmentation fault while avcodec_encode_video2 的问题似乎很匹配,但我就是看不出我遗漏了什么或出错了。

这是您可以重现崩溃的最少代码,应该使用它进行编译,

g++ -o OpenCV2FFmpeg OpenCV2FFmpeg.cpp -lopencv_imgproc -lopencv_highgui -lopencv_core -lswscale -lavutil -lavcodec -lavformat

这是我系统上的输出,

cv::Mat [width=420, height=315, depth=0, channels=3, step=1260]
I'll soon crash..
Segmentation fault

还有sample.jpg 工具的sample.jpg 文件详细信息,

~temporary/sample.jpg JPEG 420x315 420x315+0+0 8-bit sRGB 38.3KB 0.000u 0:00.000

请注意,为了简单起见,我正在尝试使用单个图像创建视频。

#include <iostream>
#include <cassert>
using namespace std;

extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libswscale/swscale.h>
    #include <libavformat/avformat.h>
}

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

const string TEST_IMAGE = "/home/baris/temporary/sample.jpg";

int main(int /*argc*/, char** argv)
{
    av_register_all();
    avcodec_register_all();

    /**
     * Initialise the encoder
     */
    AVCodec *h264encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
    AVFormatContext *cv2avFormatContext = avformat_alloc_context();

    /**
     * Create a stream and allocate frames
     */
    AVStream *h264outputstream = avformat_new_stream(cv2avFormatContext, h264encoder);
    avcodec_get_context_defaults3(h264outputstream->codec, h264encoder);
    AVFrame *sourceAvFrame = av_frame_alloc(), *destAvFrame = av_frame_alloc();
    int got_frame;

    /**
     * Pixel formats for the input and the output
     */
    AVPixelFormat sourcePixelFormat = AV_PIX_FMT_BGR24;
    AVPixelFormat destPixelFormat = AV_PIX_FMT_YUV420P;

    /**
     * Create cv::Mat
     */
    cv::Mat cvFrame = cv::imread(TEST_IMAGE, CV_LOAD_IMAGE_COLOR);
    int width = cvFrame.size().width, height = cvFrame.size().height;
    cerr << "cv::Mat [width=" << width << ", height=" << height << ", depth=" << cvFrame.depth() << ", channels=" << cvFrame.channels() << ", step=" << cvFrame.step << "]" << endl;

    h264outputstream->codec->pix_fmt = destPixelFormat;
    h264outputstream->codec->width = cvFrame.cols;
    h264outputstream->codec->height = cvFrame.rows;

    /**
     * Prepare the conversion context
     */
    SwsContext *bgr2yuvcontext = sws_getContext(width, height,
                                                sourcePixelFormat,
                                                h264outputstream->codec->width, h264outputstream->codec->height,
                                                h264outputstream->codec->pix_fmt,
                                                SWS_BICUBIC, NULL, NULL, NULL);

    /**
     * Convert and encode frames
     */
    for (uint i=0; i < 250; i++)
    {
        /**
         * Allocate source frame, i.e. input to sws_scale()
         */
        avpicture_alloc((AVPicture*)sourceAvFrame, sourcePixelFormat, width, height);

        for (int h = 0; h < height; h++)
            memcpy(&(sourceAvFrame->data[0][h*sourceAvFrame->linesize[0]]), &(cvFrame.data[h*cvFrame.step]), width*3);

        /**
         * Allocate destination frame, i.e. output from sws_scale()
         */
        avpicture_alloc((AVPicture *)destAvFrame, destPixelFormat, width, height);

        sws_scale(bgr2yuvcontext, sourceAvFrame->data, sourceAvFrame->linesize,
                  0, height, destAvFrame->data, destAvFrame->linesize);

        /**
         * Prepare an AVPacket for encoded output
         */
        AVPacket avEncodedPacket;
        av_init_packet(&avEncodedPacket);
        avEncodedPacket.data = NULL;
        avEncodedPacket.size = 0;
        // av_free_packet(&avEncodedPacket); w/ or w/o result doesn't change

        cerr << "I'll soon crash.." << endl;
        if (avcodec_encode_video2(h264outputstream->codec, &avEncodedPacket, destAvFrame, &got_frame) < 0)
            exit(1);

        cerr << "Checking if we have a frame" << endl;
        if (got_frame)
            av_write_frame(cv2avFormatContext, &avEncodedPacket);

        av_free_packet(&avEncodedPacket);
        av_frame_free(&sourceAvFrame);
        av_frame_free(&destAvFrame);
    }
}

提前致谢!

编辑:以及崩溃后的堆栈跟踪,

Thread 2 (Thread 0x7fffe5506700 (LWP 10005)):
#0  0x00007ffff4bf6c5d in poll () at /lib64/libc.so.6
#1  0x00007fffe9073268 in  () at /usr/lib64/libusb-1.0.so.0
#2  0x00007ffff47010a4 in start_thread () at /lib64/libpthread.so.0
#3  0x00007ffff4bff08d in clone () at /lib64/libc.so.6

Thread 1 (Thread 0x7ffff7f869c0 (LWP 10001)):
#0  0x00007ffff5ecc7dc in avcodec_encode_video2 () at /usr/lib64/libavcodec.so.56
#1  0x00000000004019b6 in main(int, char**) (argv=0x7fffffffd3d8) at ../src/OpenCV2FFmpeg.cpp:99

EDIT2:问题是我没有avcodec_open2() Ronald 发现的编解码器。代码的最终版本位于https://github.com/barisdemiray/opencv2ffmpeg/,存在泄漏和可能的其他问题,希望我在学习这两个库的同时对其进行改进。

【问题讨论】:

    标签: c++ c opencv ffmpeg


    【解决方案1】:

    请在崩溃后提供 gdb 回溯。我很确定它会崩溃,因为您的图片指针未对齐(avpicture_alloc() 将分配未对齐的图像,而 avcodec_encode_video2() 需要对齐的数据指针),但可能还有其他原因导致它也会崩溃。

    要对齐 AVFrame 中的数据指针,请使用 av_image_alloc() 和相关函数,其中 align=32。

    另一个问题是你没有调用avcodec_open2(h264outputstream->codec, h264encoder)。

    【讨论】:

    • 感谢您的评论 Ronald ,我现在正在添加跟踪,并将查看 av_image_alloc()
    • 好的,问题是您没有调用 avcodec_open2() - 更新了我的帖子以反映这一点。
    • 嗨罗纳德,非常感谢! av_image_alloc() 并没有太大帮助,但是是的,它是 avcodec_open2()。我正在编辑我的问题并添加最终代码。感谢您的宝贵时间。
    猜你喜欢
    • 2018-05-05
    • 2013-06-03
    • 2016-03-22
    • 2016-06-07
    • 2015-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多