ffmpeg解码h.264视频文件(QT)

44 阅读2分钟

UI

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QObject>
#include <QEvent>
#include <QString>
#include <QFileDialog>
#include <QImage>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    bool eventFilter(QObject *obj, QEvent *event);
private:
    Ui::Widget *ui;
    QString filePath_;

private slots:
    void OpenFile();
    void Play();
    void render(const QImage&);
    void resizeImage(int w,int h);
    void updateFinishLable();
};

#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>
#include <QPixmap>
#include <QImage>
#include <QListView>
#include <classl.h>


#include "ffmpegplay.h"


Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

     connect(ui->bt_choose,SIGNAL(clicked()),this,SLOT(OpenFile()));
     connect(ui->bt_play,SIGNAL(clicked()),this,SLOT(Play()));
}

bool Widget::eventFilter(QObject *target, QEvent *event){
    return false;
}

Widget::~Widget()
{
    delete ui;
}

void Widget::OpenFile(){
    QString filter = tr("mp4 file(*.mp4);;rmvb file(*.rmvb);;avi file(*.avi);;h264 file(*.h264)");
    filePath_ = QFileDialog::getOpenFileName(this,tr("打开文件"),QString(),NULL);
    int l = filePath_.lastIndexOf("/",-1);
    ui->file_name->setText(filePath_.mid(l+1).prepend("将播放"));
}
void Widget::Play(){
     int l = filePath_.lastIndexOf("/",-1);
     ui->file_name->setText(filePath_.mid(l+1).prepend("正在播放"));
     FFmpegPLay *ffmpegPLay = new FFmpegPLay(this,filePath_);
     connect(ffmpegPLay,SIGNAL(calSize(int,int)),this, SLOT(resizeImage(int,int)));
     connect(ffmpegPLay,SIGNAL(genImage(QImage)),this, SLOT(render(QImage)));
     connect(ffmpegPLay,SIGNAL(palyFinished()),this,SLOT(updateFinishLable()));
     ffmpegPLay->play();
}

void Widget::render(const QImage& img){
    ui->video_surface->setPixmap(QPixmap::fromImage(img));
}

void Widget::resizeImage(int w,int h){
    ui->video_surface->setStyleSheet("QLabel{background:#000000;}");
    ui->video_surface->setAlignment(Qt::AlignCenter);
    ui->video_surface->resize(w+10,h+5);
}

void Widget::updateFinishLable(){
     int l = filePath_.lastIndexOf("/",-1);
     ui->file_name->setText(filePath_.mid(l+1).append("已播完"));
}

解码与渲染

FFmpegPLay.h

#ifndef FFMPEGPLAY_H
#define FFMPEGPLAY_H

#ifdef __cplusplus
#include <QString>
#include <QMessageBox>
#include <QDebug>
#include <QObject>
#include <QImage>
#include <QTime>
#include <QCoreApplication>

extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
}
#endif

class FFmpegPLay : public QObject
{
    Q_OBJECT
public:
    explicit FFmpegPLay(QObject *parent,QString& h264File);
    void play();
    static void* render(void *);
public:
    QString filePath_;
private:
    void playInternal();

signals:
    void calSize(int w,int h);
    void genImage(const QImage &img);
    void palyFinished();
};

#endif // FFMPEGPLAY_H

FFmpegPLay.cpp

#include "ffmpegplay.h"
#include <stdlib.h>
#include <unistd.h>

FFmpegPLay::FFmpegPLay(QObject *parent,QString& h264File): QObject(parent)
{
    filePath_ = h264File;
}

void* FFmpegPLay::render(void *argp){
    FFmpegPLay *p = static_cast<FFmpegPLay*>(argp);
    p->playInternal();
}

void FFmpegPLay::play()
{
    pthread_t tid;
    pthread_create(&tid,NULL,render,this);
}

void FFmpegPLay::playInternal()
{
    if (filePath_.isEmpty())
    {
        QMessageBox::warning(nullptr,"warn","未选择文件",QMessageBox::Ok);
        return;
    }
    std::string input_file=filePath_.toStdString();

    av_register_all();
    AVFormatContext *pFormatCtx = avformat_alloc_context();
    if (avformat_open_input(&pFormatCtx,input_file.c_str(),nullptr,nullptr) < 0)
    {
        qDebug() << "fail to find the stream\n";
        return;
    }
    if (avformat_find_stream_info(pFormatCtx,NULL) < 0)
    {
        qDebug() << "fail to find the stream info\n";
        return;
    }
   // av_dump_format(pFormatCtx,0,input_file.c_str(),0);

    int video_stream_index = -1;
    for (size_t i =0;i < pFormatCtx->nb_streams ;++i )
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            video_stream_index = i;
            break;
        }
    }
    if (video_stream_index == -1)
    {
        qDebug() << "fail to find the video stream";
        return;
    }

    AVCodecContext *pCodecCtx = pFormatCtx->streams[video_stream_index]->codec;
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL)
    {
        qDebug() << "fail to find the decoder\n";
        return;
    }
    if (avcodec_open2(pCodecCtx,pCodec,nullptr) < 0 )
    {
        qDebug() << "fail to open the decodec\n";
        return;
    }

    AVFrame *pFrame = av_frame_alloc();
    AVFrame *pframeRGB = av_frame_alloc();
    unsigned char *out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32,pCodecCtx->width,pCodecCtx->height,1));
    av_image_fill_arrays(pframeRGB->data,pframeRGB->linesize,out_buffer,AV_PIX_FMT_RGB32,pCodecCtx->width,pCodecCtx->height,1);

    AVPacket *pkt = av_packet_alloc();
    av_init_packet(pkt);
    SwsContext *pSwsCtx = sws_alloc_context();

    emit calSize(pCodecCtx->width,pCodecCtx->height);

    pSwsCtx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,
                             pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_RGB32,
                             SWS_BICUBIC,nullptr,nullptr,nullptr);

    int got_frame = 0;
    while(av_read_frame(pFormatCtx,pkt) >= 0)
    {
        if (pkt->stream_index == video_stream_index){
            int ret = avcodec_decode_video2(pCodecCtx,pFrame,&got_frame,pkt);
            if (ret < 0)
            {
                qDebug() << "fail to decode video\n";
                return;
            }
           if (got_frame > 0)
            {
                sws_scale(pSwsCtx,(const unsigned char* const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,pframeRGB->data,pframeRGB->linesize);
                QImage image = QImage((unsigned char*)pframeRGB->data[0],pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32,nullptr,nullptr);
                emit genImage(image);
                usleep(30000);
            }
        }
        av_packet_unref(pkt);
    }

    sws_freeContext(pSwsCtx);
    av_packet_free(&pkt);
    avformat_close_input(&pFormatCtx);
    avformat_free_context(pFormatCtx);

    emit palyFinished();
}

测试结果

Screenshot from 2022-11-29 11-20-10.png