买医疗产品的网站建设/品牌营销推广方案

买医疗产品的网站建设,品牌营销推广方案,国际网站哪里做,目前比较火的电商平台有哪些一个基于 ‌FFmpeg 4.x‌ 和 ‌Qt‌ 的简单视频播放器代码示例,实现视频解码和渲染到 Qt 窗口的功能。 1)ffmpeg库界面,视频解码支持软解和硬解方式。 2)QImage/QPixmap显示视频图片。 ‌1. Qt 项目配置(.pro 文件&…

一个基于 ‌FFmpeg 4.x‌ 和 ‌Qt‌ 的简单视频播放器代码示例,实现视频解码和渲染到 Qt 窗口的功能。

1)ffmpeg库界面,视频解码支持软解和硬解方式。

2)QImage/QPixmap显示视频图片。

1. Qt 项目配置(.pro 文件)

QT       += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11INCLUDEPATH += $$PWD/ffmpeg-4.2.2-win32/include
LIBS += -L$$PWD/ffmpeg-4.2.2-win32/lib -lavcodec -lavformat -lavutil -lswscale# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \mainwindow.cpp \playimage.cpp \videodecode.cppHEADERS += \mainwindow.h \playimage.h \videodecode.hFORMS += \mainwindow.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

2. 视频解码类

‌文件 videodecode.h

#ifndef VIDEODECODE_H
#define VIDEODECODE_H//视频解码类
#include <QString>
#include <QImage>
#include <thread>extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}//流类型
enum StreamType
{StreamType_Video        = 0,StreamType_Audio        = 1,StreamType_Text         = 2,
};//格式类型
enum FormatType
{FormatType_RGB24        =   0,FormatType_RGB32        =   1,FormatType_YUV420       =   2,FormatType_YUV422       =   3,
};//文件状态
enum  FileStatus
{FileStatus_OverFileTail     =   0,  //达到文件尾FileStatus_OverFileHead     =   1,  //达到文件头FileStatus_TrigeException   =   2,  //发生异常
};//流解码回调函数
typedef void (*StreamDecodeCallback)(int nStreamType, int nFormatType, long long llDecodeTs, long long llPlayTs, int width, int height, unsigned char ** pStreamData, int * linesize, void * pUserData);//文件状态回调函数
typedef  void (*FileStatusCallback)(int FileStatus, int nErrorCode, void * pUserData);class VideoDecode
{
public:VideoDecode();~VideoDecode();public:void globalInit();//初始化ffmpeg库(整个程序中只需加载一次)void globalUnInit();//反初始化ffmpeg库(整个程序中只需加载一次)public:void setStreamDecodeCallback(StreamDecodeCallback funStreamDecodeCallback, void * userData);void setFileStatusCallback(FileStatusCallback funFileStatusCallback, void * userData);void setHWDecoder(bool flag);                 // 是否使用硬件解码器bool isHWDecoder();bool open(const QString& url);              // 打开媒体文件,或者流媒体rtmp、strp、httpvoid close();                               // 关闭bool isClose();public:void decodeProccessThread();                //解码线程static QImage ConvertRGB24FrameToQImage(unsigned char *data, int width, int height);protected:void initHWDecoder(const AVCodec *codec);bool dataCopy();    //硬件解码完成需要将数据从GPU复制到CPUvoid freeDecode();qreal rationalToDouble(AVRational* rational);private:// FFmpeg 相关对象AVFormatContext *formatCtx = nullptr;AVCodecContext *codecCtx = nullptr;AVFrame *frame = nullptr, *rgbFrame = nullptr;AVFrame *frameHW = nullptr;SwsContext *swsCtx = nullptr;uchar* buffer = nullptr;                      // YUV图像需要转换位RGBA图像,这里保存转换后的图形数据AVPacket* packet = nullptr;int videoStreamIndex = -1;  // 视频流索引qint64 totalTime    = 0;                    // 视频总时长qint64 totalFrames  = 0;                    // 视频总帧数qint64 obtainFrames = 0;                    // 视频当前获取到的帧数qint64 pts          = 0;                    // 图像帧的显示时间qreal  frameRate    = 0;                    // 视频帧率int  width = 0;         //视频分辨率大小widthint  height = 0;        //视频分辨率大小heightstd::vector<int> vecHWDeviceTypes;            // 保存当前环境支持的硬件解码器AVBufferRef* hw_device_ctx = nullptr;         // 对数据缓冲区的引用bool   hwDecoderFlag = false;                 // 记录是否使用硬件解码std::thread threadDecode;bool stopWorkFlag = true;StreamDecodeCallback funCallbackByStreamDecode = nullptr;void * userDataByStreamDecode = nullptr;FileStatusCallback funCallbackByFileStatus = nullptr;void * userDataByFileStatus = nullptr;
};#endif // VIDEODECODE_H

‌文件 videodecode.cpp

#include "videodecode.h"
#include <QTime>
#include <QDebug>
#include <QStringList>
#include <chrono>/*********************************** FFmpeg获取GPU硬件解码帧格式的回调函数 *****************************************/
static enum AVPixelFormat g_pixelFormat;/*** @brief      回调函数,获取GPU硬件解码帧的格式* @param s* @param fmt* @return*/
AVPixelFormat get_hw_format(AVCodecContext* s, const enum AVPixelFormat* fmt)
{Q_UNUSED(s)const enum AVPixelFormat* p;for (p = fmt; *p != -1; p++){if(*p == g_pixelFormat){return *p;}}qDebug() << "无法获取硬件表面格式.";         // 当同时打开太多路视频时,如果超过了GPU的能力,可能会返回找不到解码帧格式return AV_PIX_FMT_NONE;
}
/************************************************ END ******************************************************/VideoDecode::VideoDecode()
{}VideoDecode::~VideoDecode()
{
}void VideoDecode::globalInit()
{//        av_register_all();         // 已经从源码中删除/*** 初始化网络库,用于打开网络流媒体,此函数仅用于解决旧GnuTLS或OpenSSL库的线程安全问题。* 一旦删除对旧GnuTLS和OpenSSL库的支持,此函数将被弃用,并且此函数不再有任何用途。*/avformat_network_init();
}void VideoDecode::globalUnInit()
{avformat_network_deinit();
}qreal VideoDecode::rationalToDouble(AVRational* rational)
{qreal frameRate = (rational->den == 0) ? 0 : (qreal(rational->num) / rational->den);return frameRate;
}void VideoDecode::setStreamDecodeCallback(StreamDecodeCallback funStreamDecodeCallback, void * userData)
{funCallbackByStreamDecode = funStreamDecodeCallback;userDataByStreamDecode = userData;
}
void VideoDecode::setFileStatusCallback(FileStatusCallback funFileStatusCallback, void * userData)
{funCallbackByFileStatus = funFileStatusCallback;userDataByFileStatus = userData;
}//初始化硬件解码器
void VideoDecode::initHWDecoder(const AVCodec *codec)
{if(!codec) return;for(int i = 0; ; i++){const AVCodecHWConfig* config = avcodec_get_hw_config(codec, i);    // 检索编解码器支持的硬件配置。if(!config){qDebug() << "打开硬件解码器失败!";return;          // 没有找到支持的硬件配置}if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)       // 判断是否是设备类型{for(auto i : vecHWDeviceTypes){if(config->device_type == AVHWDeviceType(i))                 // 判断设备类型是否是支持的硬件解码器{g_pixelFormat = config->pix_fmt;// 打开指定类型的设备,并为其创建AVHWDeviceContext。int ret = av_hwdevice_ctx_create(&hw_device_ctx, config->device_type, nullptr, nullptr, 0);if(ret < 0){freeDecode();return ;}qDebug() << "打开硬件解码器:" << av_hwdevice_get_type_name(config->device_type);codecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx);  // 创建一个对AVBuffer的新引用。codecCtx->get_format = get_hw_format;                    // 由一些解码器调用,以选择将用于输出帧的像素格式return;}}}}
}//硬件解码完成需要将数据从GPU复制到CPU
bool VideoDecode::dataCopy()
{if(frame->format != g_pixelFormat){av_frame_unref(frame);return false;}// av_hwframe_map处理速度比av_hwframe_transfer_data快(av_hwframe_map在ffmpeg3.3以后才有)int ret = av_hwframe_map(frameHW, frame, AV_HWFRAME_MAP_DIRECT);                   // 映射硬件数据帧/*av_hwframe_map 映射硬件数据帧,第3个参数值有三种类型:AV_HWFRAME_MAP_READ:目标帧可读。AV_HWFRAME_MAP_WRITE:目标帧可写。AV_HWFRAME_MAP_DIRECT:避免数据拷贝(依赖硬件支持)‌。优先使用 AV_HWFRAME_MAP_DIRECT 减少内存拷贝开销‌。使用AV_HWFRAME_MAP_DIRECT时,你应该确保你的应用逻辑不会修改通过映射获得的软件帧内容,以避免不期望的副作用。使用AV_HWFRAME_MAP_READ时,你将获得数据的一致性但可能会有性能上的损失。*/if(ret >= 0){//映射硬件数据帧成功frameHW->width = frame->width;frameHW->height = frame->height;}else{//映射硬件数据帧失败ret = av_hwframe_transfer_data(frameHW, frame, 0);       // 将解码后的数据从GPU复制到CPU(frameHW) 比较耗时,但硬解码速度比软解码快很多if(ret < 0){av_frame_unref(frame);return false;}av_frame_copy_props(frameHW, frame);   // 仅将“metadata”字段从src复制到dst。}return true;
}void VideoDecode::setHWDecoder(bool flag)
{hwDecoderFlag = flag;
}
bool VideoDecode::isHWDecoder()
{return hwDecoderFlag;
}bool VideoDecode::open(const QString& url)
{if(url.isNull()) return false;AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;      // ffmpeg支持的硬件解码器QStringList strTypes;while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)       // 遍历支持的设备类型。{vecHWDeviceTypes.push_back(type);const char* ctype = av_hwdevice_get_type_name(type);  // 获取AVHWDeviceType的字符串名称。if(ctype){strTypes.append(QString(ctype));}}qDebug() << "支持的硬件解码器:";qDebug() << strTypes;AVDictionary* dict = nullptr;av_dict_set(&dict, "rtsp_transport", "tcp", 0);      // 设置rtsp流使用tcp打开,如果打开失败错误信息为【Error number -135 occurred】可以切换(UDP、tcp、udp_multicast、http),比如vlc推流就需要使用udp打开av_dict_set(&dict, "max_delay", "3", 0);             // 设置最大复用或解复用延迟(以微秒为单位)。当通过【UDP】 接收数据时,解复用器尝试重新排序接收到的数据包(因为它们可能无序到达,或者数据包可能完全丢失)。这可以通过将最大解复用延迟设置为零(通过max_delayAVFormatContext 字段)来禁用。av_dict_set(&dict, "timeout", "1000000", 0);         // 以微秒为单位设置套接字 TCP I/O 超时,如果等待时间过短,也可能会还没连接就返回了。// 打开输入流并返回解封装上下文int ret = avformat_open_input(&formatCtx,          // 返回解封装上下文url.toStdString().data(),  // 打开视频地址nullptr,                   // 如果非null,此参数强制使用特定的输入格式。自动选择解封装器(文件格式)&dict);                    // 参数设置// 释放参数字典if(dict){av_dict_free(&dict);}// 打开视频失败if(ret < 0){qDebug() << "Failed to avformat_open_input";return false;}// 读取媒体文件的数据包以获取流信息。ret = avformat_find_stream_info(formatCtx, nullptr);if(ret < 0){qDebug() << "Failed to avformat_find_stream_info";freeDecode();return false;}totalTime = formatCtx->duration / (AV_TIME_BASE / 1000); // 计算视频总时长(毫秒)qDebug() << QString("视频总时长:%1 ms,[%2]").arg(totalTime).arg(QTime::fromMSecsSinceStartOfDay(int(totalTime)).toString("HH:mm:ss zzz"));// 通过AVMediaType枚举查询视频流ID(也可以通过遍历查找),最后一个参数无用videoStreamIndex = av_find_best_stream(formatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);if(videoStreamIndex < 0){qDebug() << "Failed to av_find_best_stream";freeDecode();return false;}AVStream* videoStream = formatCtx->streams[videoStreamIndex];  // 通过查询到的索引获取视频流// 获取视频图像分辨率(AVStream中的AVCodecContext在新版本中弃用,改为使用AVCodecParameters)width = videoStream->codecpar->width;height = videoStream->codecpar->height;frameRate = rationalToDouble(&videoStream->avg_frame_rate);  // 视频帧率// 通过解码器ID获取视频解码器(新版本返回值必须使用const)const AVCodec* codec = avcodec_find_decoder(videoStream->codecpar->codec_id);totalFrames = videoStream->nb_frames;qDebug() << QString("分辨率:[w:%1,h:%2] 帧率:%3  总帧数:%4  解码器:%5").arg(width).arg(height).arg(frameRate).arg(totalFrames).arg(codec->name);// 分配AVCodecContext并将其字段设置为默认值。codecCtx = avcodec_alloc_context3(codec);if(!codecCtx){qDebug() << "Failed to avcodec_alloc_context3";freeDecode();return false;}// 使用视频流的codecpar为解码器上下文赋值ret = avcodec_parameters_to_context(codecCtx, videoStream->codecpar);if(ret < 0){qDebug() << "Failed to avcodec_parameters_to_context";freeDecode();return false;}codecCtx->flags2 |= AV_CODEC_FLAG2_FAST;    // 允许不符合规范的加速技巧。codecCtx->thread_count = 8;                 // 使用8线程解码if(isHWDecoder()){initHWDecoder(codec);     // 初始化硬件解码器(在avcodec_open2前调用)}// 初始化解码器上下文,如果之前avcodec_alloc_context3传入了解码器,这里设置NULL就可以ret = avcodec_open2(codecCtx, nullptr, nullptr);if(ret < 0){qDebug() << "Failed to avcodec_open2";freeDecode();return false;}// 分配AVPacket并将其字段设置为默认值。packet = av_packet_alloc();if(!packet){qDebug() << "Failed to av_packet_alloc";freeDecode();return false;}// 初始化帧和转换上下文frame = av_frame_alloc();rgbFrame = av_frame_alloc();frameHW = av_frame_alloc();int size = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height, 1);buffer = (uint8_t *)av_malloc(size + 1000);av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24,codecCtx->width, codecCtx->height, 1);/*// 初始化 SWS 上下文(YUV -> RGB 转换)swsCtx = sws_getContext(codecCtx->width, codecCtx->height, codecCtx->pix_fmt,codecCtx->width, codecCtx->height, AV_PIX_FMT_RGB24,SWS_BILINEAR, nullptr, nullptr, nullptr);*/stopWorkFlag = false;std::thread t(std::bind(&VideoDecode::decodeProccessThread,this));threadDecode = std::move(t);return true;
}void VideoDecode::close()
{stopWorkFlag = true;// 因为avformat_flush不会刷新AVIOContext (s->pb)。如果有必要,在调用此函数之前调用avio_flush(s->pb)。if(formatCtx && formatCtx->pb){avio_flush(formatCtx->pb);}if(formatCtx){avformat_flush(formatCtx);   // 清理读取缓冲}if(threadDecode.joinable()){threadDecode.join();}freeDecode();
}bool VideoDecode::isClose()
{return stopWorkFlag;
}QImage VideoDecode::ConvertRGB24FrameToQImage(unsigned char *data, int width, int height)
{// 创建 QImage 并显示QImage img(data, width, height, QImage::Format_RGB888);return img;
}void VideoDecode::decodeProccessThread()
{std::chrono::high_resolution_clock::time_point tpStart = std::chrono::high_resolution_clock::now();int nWaitTimes = 40;if(frameRate != 0){nWaitTimes = 1000.0/frameRate;}long long llDecodeTs = 0;long long llPlayTs = 0;long long llStartPlayTs = 0;bool bStartPlayTsSetValueFlag = false;bool bProccessFileTail = false;while (true){if(stopWorkFlag){break;}// 读取下一帧数据int readRet = av_read_frame(formatCtx, packet);if(readRet < 0){if (readRet == AVERROR_EOF){int ret = avcodec_send_packet(codecCtx, packet); // 读取完成后向解码器中传如空AVPacket,否则无法读取出最后几帧if(ret < 0){av_packet_unref(packet);bProccessFileTail = true;break;}}else{break;}}else{if(stopWorkFlag){break;}if(packet->stream_index == videoStreamIndex)     // 如果是图像数据则进行解码{av_packet_rescale_ts(packet, formatCtx->streams[videoStreamIndex]->time_base, codecCtx->time_base); // 转换至解码器时间基‌// 将读取到的原始数据包传入解码器int ret = avcodec_send_packet(codecCtx, packet);if(ret < 0){qDebug() << "Error sending packet";av_packet_unref(packet);continue;}}else{//其他流(比如:音频)av_packet_unref(packet);continue;}}// 接收解码后的帧(这里一次只解码一帧)int ret = avcodec_receive_frame(codecCtx, frame);if (ret == AVERROR(EAGAIN)){av_packet_unref(packet);continue;}else if (ret == AVERROR_EOF){av_packet_unref(packet);//当无法读取到AVPacket并且解码器中也没有数据时表示读取完成bProccessFileTail = true;break;}else if (ret < 0){qDebug() << "Error during decoding";av_packet_unref(packet);continue;}else{// 这样写是为了兼容软解码或者硬件解码打开失败情况AVFrame*  frameTemp = frame;if(!frame->data[0])               // 如果是硬件解码就进入{// 将解码后的数据从GPU拷贝到CPUif(!dataCopy()){av_frame_unref(frameHW);continue;}frameTemp = frameHW;}// 处理时间戳的核心逻辑int64_t raw_pts = frameTemp->pts;int64_t raw_dts = frameTemp->pkt_dts;// 处理未定义时间戳的情况if (raw_pts == AV_NOPTS_VALUE){// 使用DTS或估算PTS(需要根据帧率等参数)if(raw_dts != AV_NOPTS_VALUE){raw_pts = raw_dts;}else{raw_pts = 0;raw_dts = 0;}}// 转换为显示时间戳(秒)double display_time = raw_pts * av_q2d(codecCtx->time_base);// 转换为全局时间基(例如用于音视频同步)AVRational timeBaseTemp{1, AV_TIME_BASE};//AV_TIME_BASE_QllPlayTs = av_rescale_q(raw_pts, codecCtx->time_base, timeBaseTemp);llDecodeTs = av_rescale_q(raw_dts, codecCtx->time_base, timeBaseTemp);if(!bStartPlayTsSetValueFlag){llStartPlayTs = llPlayTs;bStartPlayTsSetValueFlag = true;}qDebug("Frame:%4d PTS:%lld display_time:%.2f DTS:%lld llPlayTs:%lld llDecodeTs:%lld packet dts:%lld pts:%lld",codecCtx->frame_number, raw_pts, display_time, raw_dts, llPlayTs, llDecodeTs, packet->dts, packet->pts);av_packet_unref(packet);  // 释放数据包,引用计数-1,为0时释放空间if(!swsCtx || (frameTemp->width != width || frameTemp->height != height)){//重新申请width = frameTemp->width;height = frameTemp->height;if(swsCtx){sws_freeContext(swsCtx);swsCtx = nullptr;}if(buffer){av_free(buffer);buffer = nullptr;}int size = av_image_get_buffer_size(AV_PIX_FMT_RGB24, frameTemp->width, frameTemp->height, 1);buffer = (uint8_t *)av_malloc(size + 1000);av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24,frameTemp->width, frameTemp->height, 1);swsCtx = sws_getCachedContext(swsCtx,frameTemp->width,                     // 输入图像的宽度frameTemp->height,                    // 输入图像的高度(AVPixelFormat)frameTemp->format,     // 输入图像的像素格式frameTemp->width,                     // 输出图像的宽度frameTemp->height,                    // 输出图像的高度AV_PIX_FMT_RGB24,                    // 输出图像的像素格式SWS_BILINEAR,                       // 选择缩放算法(只有当输入输出图像大小不同时有效),一般选择SWS_FAST_BILINEARnullptr,                            // 输入图像的滤波器信息, 若不需要传NULLnullptr,                            // 输出图像的滤波器信息, 若不需要传NULLnullptr);}//休眠等待long long llPlayTsDiff = llPlayTs - llStartPlayTs;auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - tpStart);// 计算需要等待的时间(单位:微秒)int64_t delay = llPlayTsDiff - duration.count();// 同步控制if (delay > 0){std::this_thread::sleep_for(std::chrono::microseconds(delay)); // 等待至目标时间‌}else if (delay < -100000){// 允许100ms误差阈值// 丢弃滞后帧,追赶进度‌av_frame_unref(frame);av_frame_unref(frameHW);continue;}// 转换颜色空间到 RGB24sws_scale(swsCtx, frameTemp->data, frameTemp->linesize, 0, frameTemp->height, rgbFrame->data, rgbFrame->linesize);//回调流书籍(方便渲染)if(funCallbackByStreamDecode){funCallbackByStreamDecode(StreamType_Video,FormatType_RGB24,llDecodeTs,llPlayTs,frameTemp->width,frameTemp->height,rgbFrame->data, rgbFrame->linesize, userDataByStreamDecode);}av_frame_unref(frame);av_frame_unref(frameHW);}}if(bProccessFileTail && !stopWorkFlag){if(funCallbackByFileStatus != nullptr){funCallbackByFileStatus(FileStatus_OverFileTail, 0, userDataByFileStatus);}}
}void VideoDecode::freeDecode()
{// 释放资源if (swsCtx){sws_freeContext(swsCtx);swsCtx = nullptr;}if (rgbFrame){av_frame_free(&rgbFrame);rgbFrame = nullptr;}if (frame){av_frame_free(&frame);frame = nullptr;}if(frameHW){av_frame_free(&frameHW);frameHW = nullptr;}if (codecCtx){avcodec_free_context(&codecCtx);codecCtx = nullptr;}if (formatCtx){avformat_close_input(&formatCtx);formatCtx = nullptr;}if(buffer != nullptr){av_free(buffer);buffer = nullptr;}
}

‌3.主窗口调用代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(this, SIGNAL(sgnShowImage(QImage)), this, SLOT(sltShowImage(QImage)));vdVideoDecode.globalInit();m_playImages = this->findChildren<PlayImage *>();
}MainWindow::~MainWindow()
{delete ui;vdVideoDecode.globalUnInit();
}void MainWindow::sltShowImage(QImage qimage)
{if(vdVideoDecode.isClose())return;for(int i = 0; i < m_playImages.count(); i++){m_playImages.at(i)->updateImage(qimage);}
}void MainWindow::on_pushButtonOpenFile_clicked(bool checked)
{QString filename = QFileDialog::getOpenFileName(nullptr, "Open Video File");if (!filename.isEmpty()){//vdVideoDecode.setHWDecoder(true);vdVideoDecode.setStreamDecodeCallback([](int nStreamType, int nFormatType, long long llDecodeTs, long long llPlayTs, int width, int height, unsigned char ** pStreamData, int * linesize, void * pUserData){MainWindow *pMainWindow = (MainWindow *)pUserData;QImage qimage = VideoDecode::ConvertRGB24FrameToQImage(pStreamData[0],width,height);emit pMainWindow->sgnShowImage(qimage);},this);vdVideoDecode.setFileStatusCallback([](int FileStatus, int nErrorCode, void * pUserData){qDebug()<<"file is end";},this);vdVideoDecode.open(filename);/*if(player.openFile(filename)){player.show();}*/}
}void MainWindow::on_pushButtonCloseFile_clicked()
{vdVideoDecode.close();
}

完整代码下载:https://gitee.com/byxdaz/ffmpeg-qt-player

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/898857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在百度搜索上删除与自己名字相关的资料

个人信息的网络足迹如同一张无形的网&#xff0c;将我们与世界的每一个角落紧密相连。然而&#xff0c;当某些与自己名字相关的资料不再希望被公众轻易检索到时&#xff0c;如何在百度搜索中有效“隐身”&#xff0c;成为了一个亟待解决的问题。面对复杂多变的网络环境&#xf…

[贪心算法]最长回文串 增减字符串匹配 分发饼干

1.最长回文串 我们可以存下每个字母的个数&#xff0c;然后分类讨论 如果是奇数就减一加到结果中如果是偶数就直接加入即可 最后判断长度跟原字符串的差距&#xff0c;如果小于原数组说明有奇数结果1 class Solution { public:int longestPalindrome(string s) {int ret0;//1.计…

目标检测中的非极大值抑制(NMS)原理与实现解析

一、技术背景 在目标检测任务中&#xff0c;模型通常会对同一目标生成多个重叠的候选框&#xff08;如锚框或预测框&#xff09;。非极大值抑制&#xff08;Non-Maximum Suppression, NMS&#xff09; 是一种关键的后处理技术&#xff0c;用于去除冗余的检测结果&#xff0c;保…

Linux 启动Jar脚本设置开机自启【超级详细】

Linux 启动Jar脚本&&设置开机自启【超级详细】 概要服务器开机自启服务重启脚本 概要 最近在Linux服务器中部署了一个项目&#xff08;单机版&#xff09;&#xff0c;每次更新服务的时候需要用到好几个命令&#xff0c;停止服务&#xff0c;再重启&#xff0c;并且服…

【第21节】windows sdk编程:网络编程基础

目录 引言&#xff1a;网络编程基础 一、socket介绍(套接字) 1.1 Berkeley Socket套接字 1.2 WinSocket套接字 1.3 WSAtartup函数 1.4 socket函数 1.5 字节序转换 1.6 绑定套接字 1.7 监听 1.8 连接 1.9 接收数据 1.10 发送数据 1.11 关闭套接字 二、UDP连接流程…

QT 图表(拆线图,栏状图,饼状图 ,动态图表)

效果 折线图 // 创建折线数据系列// 创建折线系列QLineSeries *series new QLineSeries;// series->append(0, 6);// series->append(2, 4);// series->append(3, 8);// 创建图表并添加系列QChart *chart new QChart;chart->addSeries(series);chart->setTit…

vue3 ts 请求封装后端接口

一 首页-广告区域-小程序 首页-广告区域-小程序 GET/home/banner1.1 请求封装 首页-广告区域 home.ts export const getHomeBannerApi (distributionSite 1) > {return http<BannerItem[]>({method: GET,url: /home/banner,data: {distributionSite,},}) }函数定…

响应式CMS架构优化SEO与用户体验

内容概要 在数字化内容生态中&#xff0c;响应式CMS架构已成为平衡搜索引擎可见性与终端用户体验的核心载体。该系统通过多终端适配技术&#xff0c;确保PC、移动端及平板等设备的内容渲染一致性&#xff0c;直接降低页面跳出率并延长用户停留时长。与此同时&#xff0c;智能S…

C++核心语法快速整理

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文主要为学过多门语言玩家快速入门C 没有基础的就放弃吧。 全部都是精华&#xff0c;看完能直接上手改别人的项目。 输出内容 std::代表了这里的cout使用的标准库&#xff0c;避免不同库中的相同命名导致混乱 …

如何让自动驾驶汽车“看清”世界?坐标映射与数据融合概述

在自动驾驶领域,多传感器融合技术是实现车辆环境感知和决策控制的关键。其中,坐标系映射和对应是多传感器融合的重要环节,它涉及到不同传感器数据在统一坐标系下的转换和匹配,以实现对车辆周围环境的准确感知。本文将介绍多传感器融合中坐标系映射和对应的数学基础和实际应…

移除idea External Liraries 中maven依赖包

问题背景 扩展包里面不停的出现已经在POM文件注释的包&#xff0c;其实是没有查询到根源位置。 在IDEA插件中搜索Maven Helper 点击pom.xml文件 会出现扩展插件 定位之后在pom中添加exclusions&#xff0c;如下代码 <dependency><groupId>com.disney.eva.framewo…

AI革命!蓝耘携手海螺AI视频,打造智能化视频新纪元

AI革命&#xff01;蓝耘携手海螺AI视频&#xff0c;打造智能化视频新纪元 前言 在这个信息爆炸的时代&#xff0c;视频已经成为我们获取信息、学习新知识的重要方式。而随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;AI与视频内容的结合为我们带来了全新的…

ElasticSearch 可观测性最佳实践

ElasticSearch 概述 ElasticSearch 是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b;本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理 PB 级别&#xff08;大数据时代&#xff09;的数据。ES 也使用 Java 开…

Excel处理控件Spire.XLS系列教程:C# 在 Excel 中添加或删除单元格边框

单元格边框是指在单元格或单元格区域周围添加的线条。它们可用于不同的目的&#xff0c;如分隔工作表中的部分、吸引读者注意重要的单元格或使工作表看起来更美观。本文将介绍如何使用 Spire.XLS for .NET 在 C# 中添加或删除 Excel 单元格边框。 安装 Spire.XLS for .NET E-…

【Java】TCP网络编程:从可靠传输到Socket实战

活动发起人小虚竹 想对你说&#xff1a; 这是一个以写作博客为目的的创作活动&#xff0c;旨在鼓励大学生博主们挖掘自己的创作潜能&#xff0c;展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴&#xff0c;那么&#xff0c;快来参加吧&#xff01…

html5炫酷的科技感3D文字效果实现详解

炫酷的科技感3D文字效果实现详解 这里写目录标题 炫酷的科技感3D文字效果实现详解项目概述核心技术实现1. 3D文字效果2. 故障艺术效果&#xff08;Glitch Effect&#xff09;3. 动态网格背景4. 扫描线效果5. 粒子效果 性能优化考虑技术难点与解决方案项目总结扩展优化方向 项目…

车道保持中车道线识别

需要让小车保持车道行驶&#xff0c;首先需要进行车道线识别。 也可参看论文&#xff08;上传到资源里&#xff09;&#xff1a;自动驾驶汽车车道检测与预测的技术解析-基于图像处理和Hough变换的方法 1 车道识别流程 想进行车道线识别&#xff0c;并且希望在图像中选择一个特…

英伟达有哪些支持AI绘画的 工程

英伟达在AI绘画领域布局广泛&#xff0c;其自研工具与第三方合作项目共同构建了完整的技术生态。以下是其核心支持AI绘画的工程及合作项目的详细介绍&#xff1a; 一、英伟达自研AI绘画工具 1. GauGAN系列 技术特点&#xff1a;基于生成对抗网络&#xff08;GAN&#xff09;&…

驱动开发的引入

1.引入 Linux内核的整体架构本就非常庞大&#xff0c;其包含的组件也非常多。而我们怎样把需要的部分都包含在内核中呢? 一种方法是把所有需要的功能都编译到Linux内核中。这会导致两个问题&#xff0c;一是生成的内核会很大&#xff0c;二是如果我们要在现有的内核中新增或删…

Android在kts中简单使用AIDL

Android在kts中简单使用AIDL AIDL相信做Android都有所了解&#xff0c;跨进程通信会经常使用&#xff0c;这里就不展开讲解原理跨进程通信的方式了&#xff0c;最近项目换成kts的方式&#xff0c;于是把aidl也换成了统一的方式&#xff0c;其中遇到了很多问题&#xff0c;这里…