You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FFmpeg解码视频并通过Qt播放时画面不刷新的修复咨询

修复FFmpeg+Qt播放画面不刷新问题

核心问题分析

  • 单QImage重复覆盖导致同步异常:子线程仅创建一个QImage,每次解码直接覆盖数据并发送同一指针,主线程可能尚未完成绘制,数据就被下一次解码覆盖,引发画面显示问题。
  • 解码速度远超UI刷新能力:解码线程全速运行,UI队列被大量帧请求塞满,无法及时完成画面刷新。
  • QLabel未强制触发重绘:设置Pixmap后未主动通知控件刷新,依赖系统自动更新可能存在延迟。
  • FFmpeg资源未正确释放:AVPacket、AVFrame未在每次使用后释放,导致内存泄漏和后续解码异常。
  • 视频流索引硬编码错误:直接判断packet->stream_index==0,未使用实际找到的视频流索引,可能选错流导致无画面。

具体修复方案

1. 每次解码创建独立QImage

子线程中每帧解码完成后创建新QImage存储数据,避免线程间数据竞争:

// 修改Ffplay::run()循环部分
while(av_read_frame(format,packet)==0){
    if(packet->stream_index == index){ // 使用找到的视频流索引而非硬编码0
        avcodec_send_packet(codec,packet);
        int ret;
        while((ret = avcodec_receive_frame(codec,frame)) >=0){
            // 为当前帧创建独立QImage
            QImage* image = new QImage(655,378,QImage::Format_RGB32);
            uint8_t* dst[]={image->bits()};
            int dstStride[4];
            av_image_fill_linesizes(dstStride,AV_PIX_FMT_RGB32,655);
            
            sws_scale(s,frame->data,frame->linesize,0,frame->height,dst,dstStride);
            emit send(image);
            
            av_frame_unref(frame); // 释放当前帧资源
        }
    }
    av_packet_unref(packet); // 每次处理完Packet必须释放
}

2. 添加帧间延迟匹配视频帧率

获取视频帧率,计算帧间隔延迟,避免解码过快:

// 在Ffplay::run()中找到视频流后添加
AVStream* videoStream = format->streams[index];
double fps = av_q2d(videoStream->avg_frame_rate);
double frameDelay = fps > 0 ? 1000 / fps : 40; // 默认40ms约25帧/秒

// 在emit send(image)后添加延迟
QThread::msleep(static_cast<unsigned long>(frameDelay));

3. 强制QLabel触发重绘

在MainWindow的paint槽函数中,设置Pixmap后主动调用刷新:

void MainWindow::paint(QImage* image){
    if(!image || image->isNull()){
        delete image;
        return;
    }
    QPixmap m=QPixmap::fromImage(*image);
    ui->label->setPixmap(m);
    ui->label->setScaledContents(true);
    ui->label->update(); // 强制触发控件重绘
    delete image; // 释放子线程传递的QImage
}

4. 完善FFmpeg资源清理

线程结束时释放所有FFmpeg资源,避免内存泄漏:

void Ffplay::run(){
    // ... 现有解码代码 ...
    
    // 循环结束后清理资源
    av_packet_free(&packet);
    av_frame_free(&frame);
    sws_freeContext(s);
    avcodec_close(codec);
    avcodec_free_context(&codec);
    avformat_close_input(&format);
    avformat_free_context(format);
}

5. 显式指定信号槽队列连接

确保跨线程信号槽使用队列模式,避免主线程阻塞:

// 主线程中修改连接代码
QObject::connect(&f,&Ffplay::send,&w,&MainWindow::paint, Qt::QueuedConnection);

完整修复后核心代码示例

Ffplay线程run()修改后

void Ffplay::run(){
    format=avformat_alloc_context();
    if(avformat_open_input(&format,"c:/Qt/project/player/movie.mp4",NULL,NULL) != 0){
        return;
    }
    if(avformat_find_stream_info(format,NULL) <0){
        return;
    }
    int index=-1;
    AVStream* videoStream = nullptr;
    for(unsigned int i=0;i<format->nb_streams;i++){
        if(format->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
            index=i;
            videoStream = format->streams[i];
            break;
        }
    }
    if(index == -1){
        return;
    }
    c=avcodec_find_decoder(videoStream->codecpar->codec_id);
    codec=avcodec_alloc_context3(c);
    avcodec_parameters_to_context(codec,videoStream->codecpar);
    if(avcodec_open2(codec,c,NULL) <0){
        return;
    }
    packet=av_packet_alloc();
    frame=av_frame_alloc();
    s=sws_getContext(codec->width,codec->height,codec->pix_fmt,655,378,AV_PIX_FMT_RGB32,SWS_FAST_BILINEAR,NULL,NULL,NULL);
    
    double fps = av_q2d(videoStream->avg_frame_rate);
    double frameDelay = fps >0 ? 1000 / fps : 40;
    
    while(av_read_frame(format,packet)==0){
        if(packet->stream_index == index){
            avcodec_send_packet(codec,packet);
            int ret;
            while((ret = avcodec_receive_frame(codec,frame)) >=0){
                QImage* image=new QImage(655,378,QImage::Format_RGB32);
                uint8_t* dst[]={image->bits()};
                int dstStride[4];
                av_image_fill_linesizes(dstStride,AV_PIX_FMT_RGB32,655);
                
                sws_scale(s,frame->data,frame->linesize,0,frame->height,dst,dstStride);
                emit send(image);
                
                av_frame_unref(frame);
                QThread::msleep(static_cast<unsigned long>(frameDelay));
            }
        }
        av_packet_unref(packet);
    }
    
    // 资源清理
    av_packet_free(&packet);
    av_frame_free(&frame);
    sws_freeContext(s);
    avcodec_close(codec);
    avcodec_free_context(&codec);
    avformat_close_input(&format);
    avformat_free_context(format);
}

内容的提问来源于stack exchange,提问作者user23143611

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:19:57