2020-04-22 06:36:41 +00:00
|
|
|
|
#include "ffmpeg.h"
|
|
|
|
|
|
|
|
|
|
FFmpegThread::FFmpegThread(QObject *parent) : QThread(parent)
|
|
|
|
|
{
|
|
|
|
|
setObjectName("FFmpegThread");
|
|
|
|
|
stopped = false;
|
|
|
|
|
isPlay = false;
|
|
|
|
|
|
|
|
|
|
frameFinish = false;
|
|
|
|
|
videoWidth = 0;
|
|
|
|
|
videoHeight = 0;
|
|
|
|
|
videoStreamIndex = -1;
|
|
|
|
|
audioStreamIndex = -1;
|
|
|
|
|
|
|
|
|
|
url = "rtsp://192.168.1.128:554/1";
|
|
|
|
|
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
avPacket = NULL;
|
2024-02-26 02:34:39 +00:00
|
|
|
|
yuvFrame = NULL;
|
|
|
|
|
rgbFrame = NULL;
|
|
|
|
|
formatCtx = NULL;
|
|
|
|
|
videoCodecCtx = NULL;
|
|
|
|
|
audioCodecCtx = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
swsContext = NULL;
|
|
|
|
|
|
|
|
|
|
options = NULL;
|
2024-02-26 02:34:39 +00:00
|
|
|
|
videoCodec = NULL;
|
|
|
|
|
audioCodec = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//初始化注册,一个软件中只注册一次即可
|
|
|
|
|
FFmpegThread::initlib();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//一个软件中只需要初始化一次就行
|
|
|
|
|
void FFmpegThread::initlib()
|
|
|
|
|
{
|
|
|
|
|
static QMutex mutex;
|
|
|
|
|
QMutexLocker locker(&mutex);
|
|
|
|
|
static bool isInit = false;
|
|
|
|
|
if (!isInit) {
|
|
|
|
|
//注册库中所有可用的文件格式和解码器
|
|
|
|
|
av_register_all();
|
|
|
|
|
//注册所有设备,主要用于本地摄像机播放支持
|
|
|
|
|
#ifdef ffmpegdevice
|
|
|
|
|
avdevice_register_all();
|
|
|
|
|
#endif
|
|
|
|
|
//初始化网络流格式,使用网络流时必须先执行
|
|
|
|
|
avformat_network_init();
|
|
|
|
|
|
|
|
|
|
isInit = true;
|
|
|
|
|
qDebug() << TIMEMS << "init ffmpeg lib ok" << " version:" << FFMPEG_VERSION;
|
|
|
|
|
#if 0
|
|
|
|
|
//输出所有支持的解码器名称
|
|
|
|
|
QStringList listCodeName;
|
|
|
|
|
AVCodec *code = av_codec_next(NULL);
|
|
|
|
|
while (code != NULL) {
|
|
|
|
|
listCodeName << code->name;
|
|
|
|
|
code = code->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qDebug() << TIMEMS << listCodeName;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FFmpegThread::init()
|
|
|
|
|
{
|
2021-11-12 06:02:52 +00:00
|
|
|
|
//在打开码流前指定各种参数比如:探测时间/超时时间/最大延时等
|
|
|
|
|
//设置缓存大小,1080p可将值调大
|
|
|
|
|
av_dict_set(&options, "buffer_size", "8192000", 0);
|
|
|
|
|
//以tcp方式打开,如果以udp方式打开将tcp替换为udp
|
|
|
|
|
av_dict_set(&options, "rtsp_transport", "tcp", 0);
|
|
|
|
|
//设置超时断开连接时间,单位微秒,3000000表示3秒
|
|
|
|
|
av_dict_set(&options, "stimeout", "3000000", 0);
|
|
|
|
|
//设置最大时延,单位微秒,1000000表示1秒
|
|
|
|
|
av_dict_set(&options, "max_delay", "1000000", 0);
|
|
|
|
|
//自动开启线程数
|
|
|
|
|
av_dict_set(&options, "threads", "auto", 0);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//打开视频流
|
2024-02-26 02:34:39 +00:00
|
|
|
|
formatCtx = avformat_alloc_context();
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
int result = avformat_open_input(&formatCtx, url.toStdString().data(), NULL, &options);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
if (result < 0) {
|
|
|
|
|
qDebug() << TIMEMS << "open input error" << url;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//释放设置参数
|
|
|
|
|
if (options != NULL) {
|
|
|
|
|
av_dict_free(&options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//获取流信息
|
2024-02-26 02:34:39 +00:00
|
|
|
|
result = avformat_find_stream_info(formatCtx, NULL);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
if (result < 0) {
|
|
|
|
|
qDebug() << TIMEMS << "find stream info error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------视频流部分开始,打个标记方便折叠代码----------
|
|
|
|
|
if (1) {
|
2024-02-26 02:34:39 +00:00
|
|
|
|
videoStreamIndex = av_find_best_stream(formatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &videoCodec, 0);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
if (videoStreamIndex < 0) {
|
|
|
|
|
qDebug() << TIMEMS << "find video stream index error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//获取视频流
|
2024-02-26 02:34:39 +00:00
|
|
|
|
AVStream *videoStream = formatCtx->streams[videoStreamIndex];
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//获取视频流解码器,或者指定解码器
|
2024-02-26 02:34:39 +00:00
|
|
|
|
videoCodecCtx = videoStream->codec;
|
|
|
|
|
videoCodec = avcodec_find_decoder(videoCodecCtx->codec_id);
|
|
|
|
|
//videoCodec = avcodec_find_decoder_by_name("h264_qsv");
|
|
|
|
|
if (videoCodec == NULL) {
|
2020-04-22 06:36:41 +00:00
|
|
|
|
qDebug() << TIMEMS << "video decoder not found";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//设置加速解码
|
2024-02-26 02:34:39 +00:00
|
|
|
|
videoCodecCtx->lowres = videoCodec->max_lowres;
|
|
|
|
|
videoCodecCtx->flags2 |= AV_CODEC_FLAG2_FAST;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//打开视频解码器
|
2024-02-26 02:34:39 +00:00
|
|
|
|
result = avcodec_open2(videoCodecCtx, videoCodec, NULL);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
if (result < 0) {
|
|
|
|
|
qDebug() << TIMEMS << "open video codec error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//获取分辨率大小
|
|
|
|
|
videoWidth = videoStream->codec->width;
|
|
|
|
|
videoHeight = videoStream->codec->height;
|
|
|
|
|
|
|
|
|
|
//如果没有获取到宽高则返回
|
|
|
|
|
if (videoWidth == 0 || videoHeight == 0) {
|
|
|
|
|
qDebug() << TIMEMS << "find width height error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString videoInfo = QString("视频流信息 -> 索引: %1 解码: %2 格式: %3 时长: %4 秒 分辨率: %5*%6")
|
2024-02-26 02:34:39 +00:00
|
|
|
|
.arg(videoStreamIndex).arg(videoCodec->name).arg(formatCtx->iformat->name)
|
|
|
|
|
.arg((formatCtx->duration) / 1000000).arg(videoWidth).arg(videoHeight);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
qDebug() << TIMEMS << videoInfo;
|
|
|
|
|
}
|
|
|
|
|
//----------视频流部分开始----------
|
|
|
|
|
|
|
|
|
|
//----------音频流部分开始,打个标记方便折叠代码----------
|
|
|
|
|
if (1) {
|
|
|
|
|
//循环查找音频流索引
|
|
|
|
|
audioStreamIndex = -1;
|
2024-02-26 02:34:39 +00:00
|
|
|
|
for (uint i = 0; i < formatCtx->nb_streams; i++) {
|
|
|
|
|
if (formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
|
2020-04-22 06:36:41 +00:00
|
|
|
|
audioStreamIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//有些没有音频流,所以这里不用返回
|
|
|
|
|
if (audioStreamIndex == -1) {
|
|
|
|
|
qDebug() << TIMEMS << "find audio stream index error";
|
|
|
|
|
} else {
|
|
|
|
|
//获取音频流
|
2024-02-26 02:34:39 +00:00
|
|
|
|
AVStream *audioStream = formatCtx->streams[audioStreamIndex];
|
|
|
|
|
audioCodecCtx = audioStream->codec;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//获取音频流解码器,或者指定解码器
|
2024-02-26 02:34:39 +00:00
|
|
|
|
audioCodec = avcodec_find_decoder(audioCodecCtx->codec_id);
|
|
|
|
|
//audioCodec = avcodec_find_decoder_by_name("aac");
|
|
|
|
|
if (audioCodec == NULL) {
|
2020-04-22 06:36:41 +00:00
|
|
|
|
qDebug() << TIMEMS << "audio codec not found";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//打开音频解码器
|
2024-02-26 02:34:39 +00:00
|
|
|
|
result = avcodec_open2(audioCodecCtx, audioCodec, NULL);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
if (result < 0) {
|
|
|
|
|
qDebug() << TIMEMS << "open audio codec error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString audioInfo = QString("音频流信息 -> 索引: %1 解码: %2 比特率: %3 声道数: %4 采样: %5")
|
2024-02-26 02:34:39 +00:00
|
|
|
|
.arg(audioStreamIndex).arg(audioCodec->name).arg(formatCtx->bit_rate)
|
|
|
|
|
.arg(audioCodecCtx->channels).arg(audioCodecCtx->sample_rate);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
qDebug() << TIMEMS << audioInfo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//----------音频流部分结束----------
|
|
|
|
|
|
|
|
|
|
//预分配好内存
|
|
|
|
|
avPacket = av_packet_alloc();
|
2024-02-26 02:34:39 +00:00
|
|
|
|
yuvFrame = av_frame_alloc();
|
|
|
|
|
rgbFrame = av_frame_alloc();
|
|
|
|
|
|
|
|
|
|
int byte = avpicture_get_size(AV_PIX_FMT_RGB32, videoWidth, videoHeight);
|
|
|
|
|
buffer = (uint8_t *)av_malloc(byte * sizeof(uint8_t));
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//定义像素格式
|
|
|
|
|
AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
|
|
|
|
|
AVPixelFormat dstFormat = AV_PIX_FMT_RGB32;
|
|
|
|
|
//通过解码器获取解码格式
|
2024-02-26 02:34:39 +00:00
|
|
|
|
srcFormat = videoCodecCtx->pix_fmt;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//默认最快速度的解码采用的SWS_FAST_BILINEAR参数,可能会丢失部分图片数据,可以自行更改成其他参数
|
|
|
|
|
int flags = SWS_FAST_BILINEAR;
|
|
|
|
|
|
|
|
|
|
//开辟缓存存储一帧数据
|
|
|
|
|
//以下两种方法都可以,avpicture_fill已经逐渐被废弃
|
2024-02-26 02:34:39 +00:00
|
|
|
|
//avpicture_fill((AVPicture *)rgbFrame, buffer, dstFormat, videoWidth, videoHeight);
|
|
|
|
|
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, dstFormat, videoWidth, videoHeight, 1);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//图像转换
|
|
|
|
|
swsContext = sws_getContext(videoWidth, videoHeight, srcFormat, videoWidth, videoHeight, dstFormat, flags, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
//输出视频信息
|
2024-02-26 02:34:39 +00:00
|
|
|
|
//av_dump_format(formatCtx, 0, url.toStdString().data(), 0);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//qDebug() << TIMEMS << "init ffmpeg finsh";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::run()
|
|
|
|
|
{
|
2023-06-21 06:32:11 +00:00
|
|
|
|
qint64 startTime = av_gettime();
|
2020-04-22 06:36:41 +00:00
|
|
|
|
while (!stopped) {
|
|
|
|
|
//根据标志位执行初始化操作
|
|
|
|
|
if (isPlay) {
|
|
|
|
|
this->init();
|
|
|
|
|
isPlay = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
frameFinish = av_read_frame(formatCtx, avPacket);
|
2021-11-03 06:13:56 +00:00
|
|
|
|
if (frameFinish >= 0) {
|
2024-02-26 02:34:39 +00:00
|
|
|
|
//下面演示倍速播放
|
|
|
|
|
if (0) {
|
|
|
|
|
double speed = 2.0;
|
|
|
|
|
avPacket->pts = avPacket->pts / speed;
|
|
|
|
|
avPacket->dts = avPacket->dts / speed;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 06:36:41 +00:00
|
|
|
|
//判断当前包是视频还是音频
|
|
|
|
|
int index = avPacket->stream_index;
|
|
|
|
|
if (index == videoStreamIndex) {
|
2020-09-07 06:03:08 +00:00
|
|
|
|
//解码视频流 avcodec_decode_video2 方法已被废弃
|
|
|
|
|
#if 0
|
2024-02-26 02:34:39 +00:00
|
|
|
|
avcodec_decode_video2(videoCodecCtx, yuvFrame, &frameFinish, avPacket);
|
2020-09-07 06:03:08 +00:00
|
|
|
|
#else
|
2024-02-26 02:34:39 +00:00
|
|
|
|
frameFinish = avcodec_send_packet(videoCodecCtx, avPacket);
|
2020-09-07 06:03:08 +00:00
|
|
|
|
if (frameFinish < 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
frameFinish = avcodec_receive_frame(videoCodecCtx, yuvFrame);
|
2020-09-07 06:03:08 +00:00
|
|
|
|
if (frameFinish < 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
2020-09-07 06:03:08 +00:00
|
|
|
|
if (frameFinish >= 0) {
|
2020-04-22 06:36:41 +00:00
|
|
|
|
//将数据转成一张图片
|
2024-02-26 02:34:39 +00:00
|
|
|
|
sws_scale(swsContext, (const uint8_t *const *)yuvFrame->data, yuvFrame->linesize, 0, videoHeight, rgbFrame->data, rgbFrame->linesize);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
|
|
|
|
|
//以下两种方法都可以
|
2024-02-26 02:34:39 +00:00
|
|
|
|
//QImage image(rgbFrame->data[0], videoWidth, videoHeight, QImage::Format_RGB32);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
QImage image((uchar *)buffer, videoWidth, videoHeight, QImage::Format_RGB32);
|
|
|
|
|
if (!image.isNull()) {
|
|
|
|
|
emit receiveImage(image);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 06:32:11 +00:00
|
|
|
|
usleep(1);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
2023-06-21 06:32:11 +00:00
|
|
|
|
#if 1
|
|
|
|
|
//延时(不然文件会立即全部播放完)
|
|
|
|
|
AVRational timeBase = {1, AV_TIME_BASE};
|
2024-02-26 02:34:39 +00:00
|
|
|
|
int64_t ptsTime = av_rescale_q(avPacket->dts, formatCtx->streams[videoStreamIndex]->time_base, timeBase);
|
2023-06-21 06:32:11 +00:00
|
|
|
|
int64_t nowTime = av_gettime() - startTime;
|
|
|
|
|
if (ptsTime > nowTime) {
|
|
|
|
|
av_usleep(ptsTime - nowTime);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-04-22 06:36:41 +00:00
|
|
|
|
} else if (index == audioStreamIndex) {
|
2023-06-21 06:32:11 +00:00
|
|
|
|
//解码音频流,自行处理
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
av_packet_unref(avPacket);
|
|
|
|
|
av_freep(avPacket);
|
2023-06-21 06:32:11 +00:00
|
|
|
|
usleep(1);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//线程结束后释放资源
|
|
|
|
|
free();
|
|
|
|
|
stopped = false;
|
|
|
|
|
isPlay = false;
|
|
|
|
|
qDebug() << TIMEMS << "stop ffmpeg thread";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::setUrl(const QString &url)
|
|
|
|
|
{
|
|
|
|
|
this->url = url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::free()
|
|
|
|
|
{
|
|
|
|
|
if (swsContext != NULL) {
|
|
|
|
|
sws_freeContext(swsContext);
|
|
|
|
|
swsContext = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (avPacket != NULL) {
|
|
|
|
|
av_packet_unref(avPacket);
|
|
|
|
|
avPacket = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
if (yuvFrame != NULL) {
|
|
|
|
|
av_frame_free(&yuvFrame);
|
|
|
|
|
yuvFrame = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
if (rgbFrame != NULL) {
|
|
|
|
|
av_frame_free(&rgbFrame);
|
|
|
|
|
rgbFrame = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
if (videoCodecCtx != NULL) {
|
|
|
|
|
avcodec_close(videoCodecCtx);
|
|
|
|
|
videoCodecCtx = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
if (audioCodecCtx != NULL) {
|
|
|
|
|
avcodec_close(audioCodecCtx);
|
|
|
|
|
audioCodecCtx = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 02:34:39 +00:00
|
|
|
|
if (formatCtx != NULL) {
|
|
|
|
|
avformat_close_input(&formatCtx);
|
|
|
|
|
formatCtx = NULL;
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
av_dict_free(&options);
|
|
|
|
|
//qDebug() << TIMEMS << "close ffmpeg ok";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::play()
|
|
|
|
|
{
|
|
|
|
|
//通过标志位让线程执行初始化
|
|
|
|
|
isPlay = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::pause()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::next()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegThread::stop()
|
|
|
|
|
{
|
|
|
|
|
//通过标志位让线程停止
|
|
|
|
|
stopped = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//实时视频显示窗体类
|
2020-09-07 06:03:08 +00:00
|
|
|
|
FFmpegWidget::FFmpegWidget(QWidget *parent) : QWidget(parent)
|
2020-04-22 06:36:41 +00:00
|
|
|
|
{
|
2020-08-27 07:38:19 +00:00
|
|
|
|
thread = new FFmpegThread(this);
|
|
|
|
|
connect(thread, SIGNAL(receiveImage(QImage)), this, SLOT(updateImage(QImage)));
|
2020-09-07 06:03:08 +00:00
|
|
|
|
image = QImage();
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FFmpegWidget::~FFmpegWidget()
|
|
|
|
|
{
|
|
|
|
|
close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::paintEvent(QPaintEvent *)
|
|
|
|
|
{
|
|
|
|
|
if (image.isNull()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//qDebug() << TIMEMS << "paintEvent" << objectName();
|
|
|
|
|
QPainter painter(this);
|
2023-01-30 03:41:18 +00:00
|
|
|
|
#if 0
|
|
|
|
|
//image = image.scaled(this->size(), Qt::KeepAspectRatio);
|
|
|
|
|
//按照比例自动居中绘制
|
|
|
|
|
int pixX = rect().center().x() - image.width() / 2;
|
|
|
|
|
int pixY = rect().center().y() - image.height() / 2;
|
|
|
|
|
QPoint point(pixX, pixY);
|
|
|
|
|
painter.drawImage(point, image);
|
|
|
|
|
#else
|
2020-04-22 06:36:41 +00:00
|
|
|
|
painter.drawImage(this->rect(), image);
|
2023-01-30 03:41:18 +00:00
|
|
|
|
#endif
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::updateImage(const QImage &image)
|
|
|
|
|
{
|
|
|
|
|
//this->image = image.copy();
|
|
|
|
|
this->image = image;
|
|
|
|
|
this->update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::setUrl(const QString &url)
|
|
|
|
|
{
|
2020-08-27 07:38:19 +00:00
|
|
|
|
thread->setUrl(url);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::open()
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << TIMEMS << "open video" << objectName();
|
|
|
|
|
clear();
|
|
|
|
|
|
2020-08-27 07:38:19 +00:00
|
|
|
|
thread->play();
|
|
|
|
|
thread->start();
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::pause()
|
|
|
|
|
{
|
2020-08-27 07:38:19 +00:00
|
|
|
|
thread->pause();
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::next()
|
|
|
|
|
{
|
2020-08-27 07:38:19 +00:00
|
|
|
|
thread->next();
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::close()
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << TIMEMS << "close video" << objectName();
|
2020-08-27 07:38:19 +00:00
|
|
|
|
if (thread->isRunning()) {
|
|
|
|
|
thread->stop();
|
|
|
|
|
thread->quit();
|
|
|
|
|
thread->wait(500);
|
2020-04-22 06:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTimer::singleShot(1, this, SLOT(clear()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FFmpegWidget::clear()
|
|
|
|
|
{
|
|
|
|
|
image = QImage();
|
|
|
|
|
update();
|
|
|
|
|
}
|