沐诗羽 2023-08-18 10:15 采纳率: 27.3%
浏览 11
已结题

在QT中使用FFmpeg把Qimage合成视频无法播放

我在QT中想把一张图片合成视频,但是不知道为什么不成功:


```c

qDebug()<<"================================================================start======================================================================";
    int ret;
    QImage image("/sdcard/test.png");
    int imagewidth = image.width();
    int imageheight = image.height();
    // 初始化 FFmpeg
    qDebug()<<"avdevice_register_all()";
    avdevice_register_all(); //初始化所有设备
    qDebug()<<"formatContext = avformat_alloc_context()";
    formatContext = avformat_alloc_context();//分配format上下文
    QString outputFileName("/sdcard/ffmep.mp4");
    qDebug()<<"avformat_alloc_output_context2(&formatContext, nullptr, nullptr, outputFileName.toUtf8().constData())";
    ret = avformat_alloc_output_context2(&formatContext, nullptr, nullptr, outputFileName.toUtf8().constData());
    qDebug()<<"ret===="<<ret;
    qDebug()<<"formatContext===="<<formatContext;
    if(!formatContext)        //如果根据文件名没有找到对应的格式则默认mpeg格式
    {
        qDebug()<<"!formatContext ======mpeg";
        ret = avformat_alloc_output_context2(&formatContext, NULL, "mpeg",  outputFileName.toUtf8().constData());    //没有找到文件类型默认mpeg(MP4)
    }
    
    qDebug()<<"formatContext->oformat = av_guess_format(nullptr, outputFileName.toUtf8().constData(), nullptr);";
    formatContext->oformat = av_guess_format(nullptr, outputFileName.toUtf8().constData(), nullptr);
    qDebug() << "avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0";
    // 打开输出文件
    if (avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0) {
        qDebug() << "Failed to open output file";
        return;
    }
    qDebug() << "AVStream* stream = avformat_new_stream(formatContext, nullptr);";
    // 创建一个AVStream对象
    AVStream* stream = avformat_new_stream(formatContext, nullptr);
    if (!stream) {
        qDebug() << "Failed to create output stream";
        return;
    }
    
    qDebug() << "AVCodecParameters* codecParameters = stream->codecpar;";
     // 配置AVCodecContext
    AVCodecParameters* codecParameters = stream->codecpar;
    codecParameters->codec_type = AVMEDIA_TYPE_VIDEO;
    codecParameters->codec_id = AV_CODEC_ID_H264; // 使用H.264编码器
    codecParameters->width = imagewidth;
    codecParameters->height = imageheight;
    qDebug() << " const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);";
    
    qDebug() << "const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);";
     // 打开编解码器
    const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);
    AVCodecContext* codecContext = avcodec_alloc_context3(codec);
    codecContext->width = imagewidth;
    codecContext->height = imageheight;
    codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
    codecContext->time_base = {1, 30}; // 设置编码器的时间基为 1秒/30帧
    
    qDebug() << "AV_PIX_FMT_YUV420P====="<<AV_PIX_FMT_YUV420P;
    qDebug() << "codecContext->pix_fmt====="<<codecContext->pix_fmt;
    qDebug() << "avcodec_open2(codecContext, codec, nullptr);";
    //设置完成编码格式以后要立刻打开,要不然调用avcodec_parameters_to_context的时候会重置编码
    ret = avcodec_open2(codecContext, codec, nullptr);
    if(ret < 0){
         qDebug() << "Failed to avcodec_open2";
         return;
    }
    qDebug() << "avcodec_parameters_to_context(codecContext, codecParameters);";
    // 将编码器参数复制到输出流
    avcodec_parameters_to_context(codecContext, codecParameters);
    // 检查编解码器支持的像素格式
    const AVPixelFormat* pixFmt = codec->pix_fmts;
    qDebug() << "while";
    while (*pixFmt != AV_PIX_FMT_NONE) {
        qDebug() << av_get_pix_fmt_name(*pixFmt);
        ++pixFmt;
    }
    
    qDebug() << " avformat_write_header(formatContext, nullptr);";
    // 写入头部信息
    avformat_write_header(formatContext, nullptr);
    
    qDebug() << "  AVFrame* frame = av_frame_alloc();";
    // 逐个写入图像帧
    AVFrame* frame = av_frame_alloc();
    
    if (!frame) {
        qDebug() << "Failed to allocate frame.";
        return;
    }
    qDebug() << "frame->format = AV_PIX_FMT_YUV420P";
    frame->format = AV_PIX_FMT_YUV420P;
    frame->width = imagewidth;
    frame->height = imageheight;
    qDebug() << "av_frame_get_buffer(frame, 32)";
    if (av_frame_get_buffer(frame, 0) < 0) {
        qDebug() << "Failed to allocate frame buffer.";
        av_frame_free(&frame);
        return;
    }
    
    qDebug() << "SwsContext* swsContext sws_getContext()";
    // 图像格式转换
    SwsContext* swsContext = sws_getContext(imagewidth, imageheight, AV_PIX_FMT_BGR32,
                                            frame->width, frame->height, AV_PIX_FMT_YUV420P,
                                            SWS_BICUBIC, nullptr, nullptr, nullptr);
    if (!swsContext) {
        qDebug() << "Failed to create SwsContext.";
        av_frame_free(&frame);
        return;
    }
    
    uint8_t* destData[4] = {frame->data[0], frame->data[1], frame->data[2], nullptr};
    int destLinesize[4] = {frame->linesize[0], frame->linesize[1], frame->linesize[2], 0};
    qDebug() << "int destLinesize[4] = {frame->linesize[0], frame->linesize[1], frame->linesize[2], 0};";
    image = image.convertToFormat(QImage::Format_RGB32);
    const uchar* bits = image.constBits();
    int bytesPerLine = image.bytesPerLine();
    qDebug() << "sws_scale(swsContext, &bits, &bytesPerLine, 0, image.height(), destData, destLinesize);";
    sws_scale(swsContext, &bits, &bytesPerLine, 0, image.height(), destData, destLinesize);
    qDebug() << "sws_freeContext(swsContext);";
    sws_freeContext(swsContext);
    
    
    qDebug() << "frame->pts = av_rescale_q(stream->nb_frames, stream->time_base, codecContext->time_base);;";
    frame->pts = av_rescale_q(stream->nb_frames, stream->time_base, codecContext->time_base);
    qDebug() << "AVPacket packet;";
    // 编码并写入视频帧
    AVPacket packet;
    av_init_packet(&packet);
    //packet.data = nullptr;
    //packet.size = 0;
    
    qDebug() << "avcodec_send_frame(codecContext, frame)";
    if (avcodec_send_frame(codecContext, frame) < 0) {
        qDebug() << "Failed to send frame for encoding.";
        av_frame_free(&frame);
        return;
    }
    qDebug() << "avcodec_receive_packet===="<<avcodec_receive_packet(codecContext, &packet);
    // 接收输出包
    while (true) {
        int ret = avcodec_receive_packet(codecContext, &packet);
        qDebug() << "ret=============="<<ret;
        if (ret == AVERROR(EAGAIN)) {
            // 需要更多输入数据,继续发送帧
            qDebug() << "ret == AVERROR(EAGAIN)=============="<<AVERROR(EAGAIN);
            break;
        } else if (ret < 0) {
            qDebug() << "Failed to receive packet.";
            av_frame_free(&frame);
            return;
        }
        // 处理输出包
        av_interleaved_write_frame(formatContext, &packet);
        av_packet_unref(&packet);
    }
    qDebug() << "av_write_trailer(formatContext)==="<<av_write_trailer(formatContext);;
    qDebug() << "av_frame_free(&frame);";
    av_frame_free(&frame);
    qDebug()<<"=============================================================stop=========================================================================";


以下是我的log:

```c
================================================================start======================================================================
avdevice_register_all()
formatContext = avformat_alloc_context()
avformat_alloc_output_context2(&formatContext, nullptr, nullptr, outputFileName.toUtf8().constData())
ret==== 0
formatContext==== 0x2f5ff770
formatContext->oformat = av_guess_format(nullptr, outputFileName.toUtf8().constData(), nullptr);
avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0
AVStream* stream = avformat_new_stream(formatContext, nullptr);
AVCodecParameters* codecParameters = stream->codecpar;
 const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);
const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);
AV_PIX_FMT_YUV420P===== 0
codecContext->pix_fmt===== 0
avcodec_open2(codecContext, codec, nullptr);
[libx264 @ 0x2f5ffec0] using cpu capabilities: none!
[libx264 @ 0x2f5ffec0] profile High, level 4.0, 4:2:0, 8-bit
avcodec_parameters_to_context(codecContext, codecParameters);
while
yuv420p
yuvj420p
yuv422p
yuvj422p
yuv444p
yuvj444p
nv12
nv16
nv21
yuv420p10le
yuv422p10le
yuv444p10le
nv20le
gray
gray10le
 avformat_write_header(formatContext, nullptr);
  AVFrame* frame = av_frame_alloc();
frame->format = AV_PIX_FMT_YUV420P
av_frame_get_buffer(frame, 32)
SwsContext* swsContext sws_getContext()
int destLinesize[4] = {frame->linesize[0], frame->linesize[1], frame->linesize[2], 0};
sws_scale(swsContext, &bits, &bytesPerLine, 0, image.height(), destData, destLinesize);
sws_freeContext(swsContext);
frame->pts = av_rescale_q(stream->nb_frames, stream->time_base, codecContext->time_base);;
AVPacket packet;
avcodec_send_frame(codecContext, frame)
avcodec_receive_packet==== -11
ret============== -11
ret == AVERROR(EAGAIN)============== -11
av_write_trailer(formatContext)=== 0
av_frame_free(&frame);
=============================================================stop=========================================================================

后面这个写入一直是-11,我没玩过FFmpeg,对这玩意不够熟悉

  • 写回答

3条回答 默认 最新

  • 比特流1024 2023-08-18 10:28
    关注

    参考gpt:
    结合自己分析给你如下建议:
    1.您没有正确地设置输出文件的格式和编码器。您的代码中,您使用了av_guess_format函数来猜测输出文件的格式,但是这个函数可能不准确,而且可能与您指定的编码器不兼容。您应该明确地指定输出文件的格式和编码器,比如使用"mp4"和"libx264"。
    2.您没有正确地设置编码器的参数和选项。您的代码中,您只设置了编码器的宽度,高度,像素格式和时间基,但是没有设置其他重要的参数和选项,比如比特率,帧率,GOP大小,最大B帧数,预设等。这些参数和选项会影响视频的质量和压缩效率,您应该根据您的需求和场景来调整它们。
    3.您没有正确地处理图片数据和视频数据之间的转换。您的代码中,您只分配了一个AVFrame对象来存储图片数据,但是没有对图片数据进行缩放和格式转换。您应该使用sws_getContext和sws_scale函数来创建一个SwsContext对象,并且用它来将图片数据从RGB格式转换为YUV格式,并且缩放到合适的大小。同时,您也应该分配一个AVPacket对象来存储视频数据,并且用avcodec_send_frame和avcodec_receive_packet函数来进行编码和写入。
    为了帮助您解决问题,我为您生成了一个可能的修改后的代码,您可以参考一下:

    qDebug()<<"================================================================start======================================================================";
    int ret;
    QImage image("/sdcard/test.png");
    int imagewidth = image.width();
    int imageheight = image.height();
    // 初始化 FFmpeg
    qDebug()<<"avdevice_register_all()";
    avdevice_register_all(); //初始化所有设备
    qDebug()<<"formatContext = avformat_alloc_context()";
    formatContext = avformat_alloc_context();//分配format上下文
    QString outputFileName("/sdcard/ffmep.mp4");
    
    // 明确指定输出文件的格式和编码器
    qDebug()<<"avformat_alloc_output_context2(&formatContext, nullptr, \"mp4\", outputFileName.toUtf8().constData())";
    ret = avformat_alloc_output_context2(&formatContext, nullptr, "mp4", outputFileName.toUtf8().constData());
    
    qDebug()<<"ret===="<<ret;
    qDebug()<<"formatContext===="<<formatContext;
    
    qDebug() << "avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0";
    // 打开输出文件
    if (avio_open(&formatContext->pb, outputFileName.toUtf8().constData(), AVIO_FLAG_WRITE) < 0) {
        qDebug() << "Failed to open output file";
        return;
    }
    
    qDebug() << "AVStream* stream = avformat_new_stream(formatContext, nullptr);";
    // 创建一个AVStream对象
    AVStream* stream = avformat_new_stream(formatContext, nullptr);
    if (!stream) {
        qDebug() << "Failed to create output stream";
        return;
    }
    
    qDebug() << "AVCodecParameters* codecParameters = stream->codecpar;";
     // 配置AVCodecContext
    AVCodecParameters* codecParameters = stream->codecpar;
    
    // 设置输出流的参数
    codecParameters->codec_type = AVMEDIA_TYPE_VIDEO;
    codecParameters->codec_id = AV_CODEC_ID_H264; // 使用H.264编码器
    codecParameters->width = imagewidth;
    codecParameters->height = imageheight;
    
    qDebug() << " const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);";
    
     // 打开编解码器
    const AVCodec* codec = avcodec_find_encoder(codecParameters->codec_id);
    
    AVCodecContext* codecContext = avcodec_alloc_context3(codec);
    
     // 设置编码器的参数和选项
     // 参考:1
    codecContext->width = imagewidth;
    codecContext->height = imageheight;
    codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
    codecContext->time_base = {1, 30}; // 设置编码器的时间基为 1秒/30帧
    codecContext->bit_rate = 1000000; // 设置编码器的比特率为 1Mbps
    codecContext->framerate = {30, 1}; // 设置编码器的帧率为 30fps
    codecContext->gop_size = 30; // 设置编码器的GOP大小为 30
    codecContext->max_b_frames = 2; // 设置编码器的最大B帧数为 2
    codecContext->qmin = 10; // 设置编码器的最小量化因子为 10
    codecContext->qmax = 51; // 设置编码器的最大量化因子为 51
    av_opt_set(codecContext->priv_data, "preset", "slow", 0); // 设置编码器的预设为 slow
    
    qDebug() << "avcodec_open2(codecContext, codec, nullptr);";
    //设置完成编码格式以后要立刻打开,要不然调用avcodec_parameters_to_context的时候会重置编码
    ret = avcodec_open2(codecContext, codec, nullptr);
    if(ret < 0){
         qDebug() << "Failed to avcodec_open2";
         return;
    }
    
    qDebug() << "avcodec_parameters_to_context(codecContext, codecParameters);";
    // 将编码器参数复制到输出流
    avcodec_parameters_to_context(codecContext, codecParameters);
    
     // 检查编解码器支持的像素格式
    const AVPixelFormat* pixFmt = codec->pix_fmts;
    qDebug() << "while";
    while (*pixFmt != AV_PIX_FMT_NONE) {
        qDebug() << av_get_pix_fmt_name(*pixFmt);
        ++pixFmt;
    }
    
    qDebug() << " avformat_write_header(formatContext, nullptr);";
    // 写入头部信息
    avformat_write_header(formatContext, nullptr);
    
     // 创建一个SwsContext对象,用于图片数据和视频数据之间的转换
     // 参考:2
     SwsContext* swsCtx = sws_getContext(imagewidth, imageheight, AV_PIX_FMT_RGB24,
                                         imagewidth, imageheight, AV_PIX_FMT_YUV420P,
                                         SWS_BICUBIC, NULL, NULL, NULL);
    
     if (!swsCtx) {
         qDebug() << "Failed to create SwsContext.";
         return;
     }
    
     // 分配两个AVFrame对象,一个用于存储图片数据,一个用于存储视频数据
     AVFrame* frameRGB = av_frame_alloc();
     AVFrame* frameYUV = av_frame_alloc();
    
     if (!frameRGB || !frameYUV) {
         qDebug() << "Failed to allocate frames.";
         return;
     }
    
     // 设置图片数据的格式和大小
     frameRGB->format = AV_PIX_FMT_RGB24;
     frameRGB->width = imagewidth;
     frameRGB->height = imageheight;
    
     // 设置视频数据的格式和大小
     frameYUV->format = AV_PIX_FMT_YUV420P;
     frameYUV->width = imagewidth;
     frameYUV->height = imageheight;
    
     // 分配图片数据和视频数据所需的内存空间
     av_image_alloc(frameRGB->data, frameRGB->linesize, imagewidth, imageheight, AV_PIX_FMT_RGB24, 32);
     av_image_alloc(frameYUV->data, frameYUV->linesize, imagewidth, imageheight, AV_PIX_FMT_YUV420P, 32);
    
     // 分配一个AVPacket对象,用于存储视频数据
     AVPacket* pkt = av_packet_alloc();
    
     if (!pkt) {
         qDebug() << "Failed to allocate packet.";
         return;
     }
    
    
    
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 8月18日
  • 创建了问题 8月18日

悬赏问题

  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥30 BC260Y用MQTT向阿里云发布主题消息一直错误
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM
  • ¥15 划分vlan后不通了
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)