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

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

https://cloud.tencent.com/developer/article/2211590

Разработка программного обеспечения 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

1. Предисловие

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

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

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

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

Язык кода:javascript
копировать
версия 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%. Если декодировать через графический процессор, загрузка процессора будет очень низкой, что освобождает больше времени для других дел.

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

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

Язык кода:javascript
копировать
ffmpeg\doc\examples\decode_video.c
ffmpeg\doc\examples\hw_decode.c

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

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

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

2.2 Рендеринг

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

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

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

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

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

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

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

Ниже приведен основной процесс декодирования с помощью программного обеспечения ffmpeg:

Язык кода:javascript
копировать
//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 Эффект операции

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

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

Язык кода:javascript
копировать
//Кодировка указанного файла — 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("Размер видеокадра (в пикселях)): (ширина х высота)%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, но не копирует его. Она просто указывает указатель данных в структуре изображения на данные 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);
    }

    вернуть 0;
}

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

Язык кода:javascript
копировать
#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;  //Текущая позиция поиска
	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 рендеринг виджета

Язык кода:javascript
копировать
#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(); //Обновление вызова будет выполнено функция PaintEvent
}


/*
Функция: Получить исходные данные изображения
*/
QImage VideoFrameDisplay::GetImage()
{
    return src_mImage.copy();
}

/*
Функция: Событие двойного щелчка мыши
*/
void VideoFrameDisplay::mouseDoubleClickEvent(QMouseEvent *e)
{
    emit s_VideoWidgetEvent(1);
}

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

Язык кода:javascript
копировать
#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)

Язык кода:javascript
копировать
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*
}
boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose