Разработка программного обеспечения QT - проектирование видеоплеера на основе FFMPEG - изображение программного решения (1)

Разработка программного обеспечения QT - дизайн видеоплеера на основе FFMPEG - видео программного решения ЦП (1)
https://xiaolong.blog.csdn.net/article/details/126832537

Разработка программного обеспечения QT - разработка видеоплеера на основе FFMPEG - видео о жестком решении GPU (2)
https://xiaolong.blog.csdn.net/article/details/126833434

Разработка программного обеспечения QT - проектирование видеоплеера на основе FFMPEG - декодирование аудио (3)
https://xiaolong.blog.csdn.net/article/details/126836582

Разработка программного обеспечения QT - проектирование видеоплеера на основе FFMPEG - видео рендеринга OpenGL (4)
https://xiaolong.blog.csdn.net/article/details/126842988

Разработка программного обеспечения QT - дизайн видеоплеера на основе FFMPEG - проигрыватель потокового мультимедиа (5)
https://xiaolong.blog.csdn.net/article/details/126915003

Разработка программного обеспечения QT - дизайн видеоплеера на основе FFMPEG - видеоплеер (6)
https://blog.csdn.net/xiaolong1126626497/article/details/126916817

Введение

Говоря о ffmpeg, поскольку он занимается разработкой аудио и видео, вы должны были о нем слышать. FFmpeg предоставляет очень продвинутую библиотеку аудио/видео кодеков и поддерживает кроссплатформенность.

Сейчас в Интернете есть много статей и руководств, связанных с ffmpeg, сам ffmpeg в основном используется для декодирования и кодирования видео и аудио. Он не обеспечивает рендеринга изображения, вывода звука и других функций. Чтобы разработать собственный проигрыватель, вам сначала понадобятся некоторые другие знания, чтобы сотрудничать с вызовом ffmpeg для выполнения задачи.

Чтобы познакомить с использованием ffmpeg просто и быстро, я пройдусь по нескольким статьям и напишу несколько кейсов, чтобы шаг за шагом продемонстрировать использование ffmpeg, и, наконец, завершу полную разработку плеера, не привлекая теоретических знаний (слишком много теоретических знаний в Интернете), в основном на основе кода и функций.

Введение в среду, которую я использовал для разработки видеоплеера, здесь:

ffmpeg版本:  4.2.2
Qt版本    :  5.12.6
编译器类型 : MinGW32bit 

Пошаговое контент-планирование и кейсы этих статей таковы:

(1) Используйте ffmpeg для декодирования видео, рендеринга декодированного изображения с помощью QWidget, поддержки перехода на индикатор выполнения, отображения индикатора выполнения, отображения общего времени и отображения базовой информации о видео.

Особенности: Использование программного декодирования (ЦП), декодирование только данных изображения, игнорируя аудиоданные, в основном демонстрирует основной процесс использования ffmpeg, как завершить декодирование видео с помощью ffmpeg, преобразовать формат пикселей изображения и, наконец, завершить рендеринг изображения.

(2) Используйте интерфейс аппаратного ускорения ffmpeg для завершения декодирования видео, поддержки обнаружения методов ускорения, поддерживаемых текущим оборудованием, копирования данных из графического процессора в ЦП после декодирования, завершения преобразования пикселей, а затем рендеринга изображений с помощью QWidget, поддержки перехода индикатора выполнения, индикатора выполнения Дисплей, отображение общего времени, отображение основной информации о видео.

Особенности: Использование аппаратного ускоренного декодирования (GPU), декодирование только данных изображения, игнорируя аудиоданные, в основном демонстрирует базовый процесс использования аппаратного декодирования ffmpeg, как завершить декодирование видео с помощью ffmpeg, преобразовать формат пикселей изображения и, наконец, завершить рендеринг изображения.

(3) Используйте ffmpeg для декодирования аудиопакетов в видео и воспроизведения аудиоданных через QAudioOutput.

Особенности: декодировать только аудиоданные и игнорировать данные видеоизображения В основном демонстрируется базовый процесс использования ffmpeg, как завершить декодирование аудиоданных с помощью ffmpeg, преобразовать формат аудиоданных и, наконец, воспроизвести их через QAudioOutput.

(4) Используйте интерфейс аппаратного ускорения ffmpeg для завершения декодирования видео, поддержки обнаружения методов ускорения, поддерживаемых текущим оборудованием, а также QOpenGLWidgetподдержки скачка индикатора выполнения, отображения индикатора выполнения, отображения общего времени и отображения основной информации о видео путем рендеринга данных декодированного изображения.

Особенности: Использование аппаратного ускоренного декодирования (GPU), рендеринг OpenGL, только декодирование данных изображения, игнорирование аудиоданных, в основном демонстрирует основной процесс использования аппаратного декодирования ffmpeg и рендеринга OpenGL.

(5) Добавить поддержку воспроизведения потокового мультимедиа в пример (4), поддерживать распространенные форматы потокового мультимедиа, такие как rtmp, rtsp, HLS (протокол HTTP), использовать интерфейс аппаратного ускорения ffmpeg для завершения декодирования видео и поддерживать обнаружение текущей аппаратной поддержки. ускоренный способ рендеринга QOpenGLWidgetданных декодированного изображения.

Особенности: Использование аппаратного ускоренного декодирования (GPU), рендеринг OpenGL, только декодирование данных изображения, игнорирование аудиоданных, в основном демонстрирует основной процесс использования аппаратного декодирования ffmpeg и рендеринга OpenGL.

(6) Объединение примеров (3) и (5), добавление декодирования и воспроизведения аудиопакетов, использование интерфейса аппаратного ускорения ffmpeg для завершения декодирования видео, поддержка обнаружения методов ускорения, поддерживаемых текущим оборудованием, и рендеринг данных декодированного изображения. QOpenGLWidgetчерез QAudioOutput воспроизводит аудиоданные. Поддержка скачка индикатора выполнения, отображения индикатора выполнения, отображения общего времени, отображения основной информации о видео.

Особенности: Использование аппаратного ускоренного декодирования (GPU), рендеринг OpenGL, декодирование только данных изображения, игнорирование аудиоданных, в основном демонстрирует базовый процесс использования аппаратного декодирования ffmpeg и рендеринга OpenGL, а также процесс воспроизведения аудиоданных через QAudioOutput.

(7) В сочетании с предыдущими примерами спроектируйте завершенный видеоплеер.

2. Декодирование и рендеринг

Если вы хотите сделать видеоплеер, в основном решите три проблемы: (1) декодирование (2) рендеринг (3) синхронизация аудио и видео.

2.1 Расшифровка

ffmpeg поддерживает чистое программное декодирование и декодирование с аппаратным ускорением. Поскольку чисто программное декодирование зависит от ЦП, если программное декодирование видео с большим разрешением (4K и выше) будет занимать много ЦП, а скорость декодирования будет относительно низкой, плюс время рендеринга, общий видеоплеер заморозит Феномен. Если вы используете аппаратное ускорение (GPU) для декодирования, время декодирования будет значительно сокращено. Мой компьютер - маломощный процессор i7. Я тестировал его на своем компьютере: для видео с разрешением 3840x2160 требуется около 300 мс для декодирования кадра программным путем, если включено аппаратное ускорение декодирования, для декодирования требуется около 10 мс. кадр.Разница в скорости очень большая. А при мягком декодировании загрузка ЦП почти 100%, если декодировать на GPU, нагрузка на ЦП будет очень низкой, что может высвободить больше времени для других дел.

Разница между софт-решением и хард-решением не очень большая, т.к. ffmpeg уже инкапсулировал все API, его нужно только вызывать, и много чего понимать на нижнем уровне не нужно, так что это очень удобно развиваться.

В исходном коде ffmpeg есть много примеров, включая примеры декодирования видео.

ffmpeg\doc\examples\decode_video.c
ffmpeg\doc\examples\hw_decode.c

Эти два примера имеют большое справочное значение. Среди hw_decode.c них пример декодирования с аппаратным ускорением ffmpeg. На этом примере вы можете понять, как ffmpeg вызывает GPU для аппаратного декодирования.

Конечно, ffmpeg также содержит проигрыватель командной строки, исходный код — ffplay.c, этот код очень хорошо реализован, это просто проигрыватель, но ffplay.c имеет больше кодов, помимо собственного вызова API ffmpeg, рендеринг Часть реализована через SDL.Если вы не очень хорошо знакомы с ffmpeg и SDL на ранней стадии, может быть не очень эффективно переходить напрямую к ffplay.c.

Лучше всего начать с простого, разбираться шаг за шагом и, наконец, перейти к ffplay.c, так эффект будет намного лучше.

2.2 Рендеринг

Сам ffmpeg — это всего лишь библиотека для декодирования и кодирования, а рендеринг декодированного изображения должен быть реализован сам по себе. Так называемый рендеринг заключается в отображении данных изображения, полученных после того, как ffmpeg декодирует видео. Рендеринг также делится на программный рендеринг и рендеринг с аппаратным ускорением. В настоящее время мой пользовательский интерфейс здесь создан с помощью Qt.Существует множество способов отображения изображений в Qt, таких как рисование непосредственно через Qwidget, отображение с помощью QLabel и т. д. Этот метод является наиболее традиционным и самым простым методом.Этот метод использует ЦП для рисования, что занимает много ЦП, и его необходимо отображать через Qwidget, QLabel и т. д., а данные, декодированные с помощью ffmpeg, необходимо преобразуется в пиксельный формат. Переупаковка в QImageформат занимает много времени. Если вы хотите уменьшить использование ЦП и ускорить рендеринг, вы можете использовать для рендеринга OpenGL, который упакован в Qt QOpenGLWidget, и относительно удобно вызывать OpenGL.

3. Дизайн видеоплеера

3.1 Описание конструкции

Используйте ffmpeg для декодирования видео, рендеринга декодированного изображения с помощью QWidget, поддержки перехода индикатора выполнения, отображения индикатора выполнения, отображения общего времени и отображения основной информации о видео.

Особенности: Использование программного декодирования (ЦП), декодирование только данных изображения, игнорируя аудиоданные, в основном демонстрирует основной процесс использования ffmpeg, как завершить декодирование видео с помощью ffmpeg, преобразовать формат пикселей изображения и, наконец, завершить рендеринг изображения.

Декодирование видео использует независимый подпоток, и данные изображения, полученные после декодирования, передаются в интерфейс пользовательского интерфейса для рендеринга посредством передачи слота сигнала.

3.2 Процесс декодирования

Ниже приведен основной поток программного декодирования ffmpeg:

//1.打开多媒体流,并且获取一些信息
avformat_open_input(&format_ctx, m_MediaFile, nullptr, nullptr)


//2. 读取媒体文件的数据包以获取流信息
avformat_find_stream_info(format_ctx, nullptr)


//3. 查找解码器
AVCodec *video_pCodec=avcodec_find_decoder(stream->codecpar->codec_id);

//4. 打开解码器
avcodec_open2(stream->codec,video_pCodec,nullptr)

//5. 读取一帧数据
av_read_frame(format_ctx, &pkt)

//6. 发送视频帧
avcodec_send_packet(format_ctx->streams[video_stream_index]->codec,&pkt)

//7. 对视频帧进行解码
avcodec_receive_frame(format_ctx->streams[video_stream_index]->codec, SRC_VIDEO_pFrame)

//8. 转换像素格式
sws_scale(img_convert_ctx,
	   (uint8_t const **) SRC_VIDEO_pFrame->data,
	   SRC_VIDEO_pFrame->linesize, 0, video_height, RGB24_pFrame->data,
	   RGB24_pFrame->linesize);

//9. 渲染

Что здесь занимает больше времени, так это sws_scaleто, что чем выше разрешение видео, тем больше времени это занимает. Во-вторых avcodec_receive_frame, и рендеринг, если софт-декодированное видео превышает 4K, декодирование займет много времени, если разрешение ниже 4K, время, затраченное на декодирование, приемлемо.

3.3 Эффект операции

изображение-20220913140928520

4. Исходный код

4.1 Декодирование thread.cpp

//指定文件的编码为UTF-8
#pragma execution_character_set("utf-8")

#include "ReverseDecodThread.h"

ReverseDecodThread::ReverseDecodThread()
{
    
    
    qDebug() << "FFMPEG版本信息:" << av_version_info();
}

ReverseDecodThread::~ReverseDecodThread()
{
    
    

}


/*
功能: 设置媒体文件
*/
int ReverseDecodThread::set_VideoFile(QString media)
{
    
    
    //打开媒体文件
    QByteArray array=media.toUtf8();
    strncpy(m_MediaFile, array.data(), sizeof(m_MediaFile));
}


void ReverseDecodThread::SetSate(int run)
{
    
    
	m_run = run;
}

int ReverseDecodThread::GetSate()
{
    
    
	return m_run;
}


//跳转视频帧
void ReverseDecodThread::SetSeekPos(qint64 pos)
{
    
    
	is_CurrentSeekPos = 1;
    m_n64CurrentSeekPos = pos;
    m_run=1;  //运行状态

    //获取系统本地时间
    play_base_time=QDateTime::currentMSecsSinceEpoch();
}


void ReverseDecodThread::PausePlay()
{
    
    
	m_run = 2;
}

void ReverseDecodThread::StopPlay()
{
    
    
	m_run = 0;
}

void ReverseDecodThread::LogSend(QString text)
{
    
    
	qDebug() << text;
}


//线程执行起点
void ReverseDecodThread::run()
{
    
    
    LogSend("开始播放视频.\n");
    StartPlay();
}


//播放视频
int ReverseDecodThread::StartPlay()
{
    
    
    //1.打开多媒体流,并且获取一些信息
    if(avformat_open_input(&format_ctx, m_MediaFile, nullptr, nullptr) != 0)
    {
    
    
         LogSend(tr("无法打开视频文件: %1").arg(m_MediaFile));
         return -1;
    }

    //2. 读取媒体文件的数据包以获取流信息
    if(avformat_find_stream_info(format_ctx, nullptr) < 0)
    {
    
    
        LogSend(tr("无法获取流信息.\n"));
        return -1;
    }

    //3.打印视频的信息
    LogSend(tr("视频中流的数量: %1\n").arg(format_ctx->nb_streams));
    for(int i = 0; i < format_ctx->nb_streams; ++i)
    {
    
    
        const AVStream* stream = format_ctx->streams[i];
        if(stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
    
    
            //查找解码器
            AVCodec *video_pCodec=avcodec_find_decoder(stream->codecpar->codec_id);
            //打开解码器
            if(avcodec_open2(stream->codec,video_pCodec,nullptr)!=0)
            {
    
    
                  LogSend(tr("解码器打开失败.\n"));
                  return -1;
            }
            video_stream_index = i;
            //得到视频帧的宽高
            video_width=stream->codecpar->width;
            video_height=stream->codecpar->height;

            LogSend(tr("视频帧的尺寸(以像素为单位): (宽X高)%1x%2 像素格式: %3\n").arg(
                stream->codecpar->width).arg(stream->codecpar->height).arg(stream->codecpar->format));
        }
    }

    if (video_stream_index == -1)
    {
    
    
         LogSend("没有检测到视频流.\n");
         return -1;
    }

    AVRational frameRate = format_ctx->streams[video_stream_index]->avg_frame_rate;

    /*设置视频转码器*/
    SRC_VIDEO_pFrame = av_frame_alloc();
    RGB24_pFrame = av_frame_alloc();// 存放解码后YUV数据的缓冲区

    //将解码后的YUV数据转换成RGB24
    img_convert_ctx = sws_getContext(video_width, video_height,
            format_ctx->streams[video_stream_index]->codec->pix_fmt,video_width, video_height,
            AV_PIX_FMT_RGB24, SWS_BICUBIC, nullptr, nullptr, nullptr);

    //计算RGB图像所占字节大小
    int numBytes=avpicture_get_size(AV_PIX_FMT_RGB24,video_width,video_height);

    //申请空间存放RGB图像数据
    out_buffer_rgb = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    // avpicture_fill函数将ptr指向的数据填充到picture内,但并没有拷贝,只是将picture结构内的data指针指向了ptr的数据
    avpicture_fill((AVPicture *) RGB24_pFrame, out_buffer_rgb, AV_PIX_FMT_RGB24,
            video_width, video_height);

    qDebug()<<"format_ctx->duration:"<<format_ctx->duration;

    //获取系统本地时间
    play_base_time=QDateTime::currentMSecsSinceEpoch();

    //表示视频加载成功
    while(m_run)
    {
    
    
        if(m_run == 2)
        {
    
    
            msleep(100); //暂停播放
			continue;
        }

		if (is_CurrentSeekPos)
		{
    
    
			is_CurrentSeekPos = 0;
            //偏移到指定位置再开始解码    AVSEEK_FLAG_BACKWARD 向后找最近的关键帧
            av_seek_frame(format_ctx, -1, m_n64CurrentSeekPos* AV_TIME_BASE, AVSEEK_FLAG_BACKWARD);
            qDebug()<<"跳转的位置:"<<m_n64CurrentSeekPos;
		}

        double video_clock;
        AVPacket pkt;

        //1. 读取一帧数据
        if(av_read_frame(format_ctx, &pkt) < 0)
        {
    
    
            m_run=2; //设置为暂停状态
            qDebug()<<"数据读取完毕.";
            continue;
        }

        if(pkt.stream_index == video_stream_index)
        {
    
    
            //当前时间
           video_clock = av_q2d(format_ctx->streams[video_stream_index]->time_base) * pkt.pts;

           qDebug()<<"pkt.pts:"<<pkt.pts<<"video_clock:"<<video_clock;


           //解码视频 frame
           //2. 发送视频帧
            if (avcodec_send_packet(format_ctx->streams[video_stream_index]->codec,&pkt) != 0)
            {
    
    
                av_packet_unref(&pkt);//不成功就释放这个pkt
                continue;
            }

            //3. 接受后对视频帧进行解码
            if (avcodec_receive_frame(format_ctx->streams[video_stream_index]->codec, SRC_VIDEO_pFrame) != 0)
            {
    
    
                av_packet_unref(&pkt);//不成功就释放这个pkt
                continue;
            }

            //4. 转格式
           sws_scale(img_convert_ctx,
                   (uint8_t const **) SRC_VIDEO_pFrame->data,
                   SRC_VIDEO_pFrame->linesize, 0, video_height, RGB24_pFrame->data,
                   RGB24_pFrame->linesize);


    
           //5. 加载图片数据
           QImage image(out_buffer_rgb,video_width,video_height,QImage::Format_RGB888);


           //通过pts同步视频帧--显示视频帧
//           while (true)
//           {
    
    
//                qint64 t1=QDateTime::currentMSecsSinceEpoch();
//                qint64 t2=t1-play_base_time;

//                qDebug()<<"t1:"<<t1;
//                qDebug()<<"t2:"<<t2;
//                qDebug()<<"video_clock:"<<video_clock*1000;

//                if(t2>=video_clock*1000)
//                {
    
    
//                    break;
//                }
//                else
//                {
    
    
//                     QThread::msleep(1);
//                }
//           }

           //通知界面更新
           VideoDataOutput(image.copy());

           //时间信号
           sig_getCurrentTime(video_clock, format_ctx->duration *1.0 / AV_TIME_BASE);

          // QThread::msleep(40);
        }
               //释放包
           av_packet_unref(&pkt);

    }

    LogSend("视频音频解码播放器的线程退出成功.\n");

    if(SRC_VIDEO_pFrame) av_frame_free(&SRC_VIDEO_pFrame);
    if(RGB24_pFrame) av_frame_free(&RGB24_pFrame);
    if(img_convert_ctx)sws_freeContext(img_convert_ctx);
    if(out_buffer_rgb)av_free(out_buffer_rgb);

    SRC_VIDEO_pFrame=nullptr;
    RGB24_pFrame=nullptr;
    img_convert_ctx=nullptr;
    out_buffer_rgb=nullptr;

    if(format_ctx)
    {
    
    
        avformat_close_input(&format_ctx);//释放解封装器的空间,以防空间被快速消耗完
        avformat_free_context(format_ctx);
    }

    return 0;
}

4.2 Декодирование thread.h

#ifndef VIDEO_PLAY_H
#define VIDEO_PLAY_H

#include <QThread>
#include <qdebug.h>
#include <QImage>
#include <QDateTime>

extern "C" {
    
    
#include <libavutil/opt.h>
#include <libavutil/mem.h>
#include <libavutil/fifo.h>
#include <libavutil/pixfmt.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
}


//视频音频解码线程
class ReverseDecodThread: public QThread
{
    
    
    Q_OBJECT
public:

    //构造函数
    ReverseDecodThread();
    ~ReverseDecodThread();
	char m_MediaFile[1024];
	int m_run; //1表示运行 0表示停止 2表示暂停
    double m_n64CurrentSeekPos = 0;  //当前seek位置
	bool is_CurrentSeekPos = 0; //1需要跳转 0不需要

	void SetSate(int run);
	int GetSate();
	void SetSeekPos(qint64 pos);
	void PausePlay();
	void StopPlay();
	void LogSend(QString text);

    //加载视频文件
    int set_VideoFile(QString media);

protected:
    void run();
	int StartPlay();
signals:
    void sig_getCurrentTime(double Sec, double total_Sec);
    void VideoDataOutput(QImage); //输出信号
private:
    int video_width=0;
    int video_height=0;
    AVFormatContext *format_ctx=nullptr;
    int video_stream_index = -1;
    AVFrame *RGB24_pFrame = nullptr;
    AVFrame *SRC_VIDEO_pFrame= nullptr;
    uint8_t *out_buffer_rgb= nullptr;
    struct SwsContext *img_convert_ctx=nullptr;  //用于解码后的视频格式转换

    double video_clock_tmp;

    qint64 play_base_time=0;
};
#endif // VIDEO_PLAY_H

4.3 визуализация виджета

#include "VideoFrameDisplay.h"

#include <QPainter>

VideoFrameDisplay::VideoFrameDisplay(QWidget *parent) :
    QWidget(parent)
{
    
    
    m_nRotateDegree=0;
    this->setMouseTracking(true);
}

VideoFrameDisplay::~VideoFrameDisplay()
{
    
    

}

void VideoFrameDisplay::Set_Rotate(int Rotate)
{
    
    
    m_nRotateDegree=Rotate;
}

void VideoFrameDisplay::paintEvent(QPaintEvent *event)
{
    
    
    QPainter painter(this);
	
	painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHint(QPainter::TextAntialiasing);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    painter.setRenderHint(QPainter::HighQualityAntialiasing);
	
    painter.setBrush(Qt::black);
    painter.drawRect(0,0,this->width(),this->height()); //先画成黑色

    if (mImage.size().width() <= 0) return;

    //将图像按比例缩放成和窗口一样大小
    QImage img = mImage.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); 

    //画面旋转
    if(m_nRotateDegree > 0)
    {
    
    
        QTransform matrix;
        matrix.rotate(m_nRotateDegree);
        img = img.transformed(matrix, Qt::SmoothTransformation);
    }

    int x = this->width() - img.width();
    int y = this->height() - img.height();

    x /= 2;
    y /= 2;

    painter.drawImage(QPoint(x,y),img); //画出图像
}


void VideoFrameDisplay::slotSetOneFrame(QImage img)
{
    
    
    src_mImage =mImage = img;
    update(); //调用update将执行 paintEvent函数
}


/*
功能: 获取原图数据
*/
QImage VideoFrameDisplay::GetImage()
{
    
    
    return src_mImage.copy();
}

/*
功能: 鼠标双击事件
*/
void VideoFrameDisplay::mouseDoubleClickEvent(QMouseEvent *e)
{
    
    
    emit s_VideoWidgetEvent(1);
}

4.4 основной поток пользовательского интерфейса widget.cpp

#include "widget.h"
#include "ui_widget.h"

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

    ui->horizontalSlider_time->installEventFilter(this);

    //关联视频解码器
    connect(&DecodeWorkThread, SIGNAL(VideoDataOutput(QImage)), ui->widget_video, SLOT(slotSetOneFrame(QImage)));

    //当前时间
    connect(&DecodeWorkThread, SIGNAL(sig_getCurrentTime(double, double)), this, SLOT(slotGetCurrentTime(double, double)));
}


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


void Widget::slotGetCurrentTime(double pts, double duration)
{
    
    
    ui->horizontalSlider_time->setMaximum(duration);
    ui->horizontalSlider_time->setMinimum(0);
    ui->horizontalSlider_time->setValue(pts);

    ui->label_duration->setText(QString("%1/%2").arg(pts).arg(duration));
}


void Widget::on_pushButton_play_clicked()
{
    
    
    DecodeWorkThread.SetSate(0);
    DecodeWorkThread.quit();
    DecodeWorkThread.wait();

    DecodeWorkThread.SetSate(1);

    QString filename = QFileDialog::getOpenFileName(this, "选择打开的文件", "C:/", tr("*.*"));
    DecodeWorkThread.set_VideoFile(filename);
    DecodeWorkThread.start();
}


void Widget::on_pushButton_pause_clicked()
{
    
    
    if (DecodeWorkThread.GetSate() == 2)
    {
    
    
        DecodeWorkThread.SetSate(1);
    }
    else if(DecodeWorkThread.GetSate() == 1)
    {
    
    
        DecodeWorkThread.SetSate(2);
    }
}


void Widget::on_pushButton_stop_clicked()
{
    
    
     DecodeWorkThread.SetSate(0);
}


bool Widget::eventFilter(QObject *obj, QEvent *event)
{
    
    
    //解决QSlider点击不能到鼠标指定位置的问题
    if(obj==ui->horizontalSlider_time)
    {
    
    
        if (event->type()==QEvent::MouseButtonPress) //判断类型
        {
    
    
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            if (mouseEvent->button() == Qt::LeftButton)	//判断左键
            {
    
    
               int value = QStyle::sliderValueFromPosition(ui->horizontalSlider_time->minimum(), ui->horizontalSlider_time->maximum(), mouseEvent->pos().x(), ui->horizontalSlider_time->width());
               ui->horizontalSlider_time->setValue(value);

               qDebug()<<"value:"<<value;
               //跳转到指定位置
               DecodeWorkThread.SetSeekPos(ui->horizontalSlider_time->value());
            }
        }
    }
    return QObject::eventFilter(obj,event);
}

4.5 pro файл (загрузить библиотеку ffmpeg)

win32
{
    
    
    message('运行win32版本')
    INCLUDEPATH+=C:/FFMPEG/ffmpeg_x86_4.2.2/include
    LIBS+=C:/FFMPEG/ffmpeg_x86_4.2.2/bin/av*
    LIBS+=C:/FFMPEG/ffmpeg_x86_4.2.2/bin/sw*
    LIBS+=C:/FFMPEG/ffmpeg_x86_4.2.2/bin/pos*
}

рекомендация

отblog.csdn.net/xiaolong1126626497/article/details/126832537