QT+ffmpeg学习笔记-自制一个简易播放器(一)

此文章为自用笔记,如有错误欢迎指出!

本文简要介绍了在Qt环境下使用FFmpeg库实现视频播放的步骤。首先配置FFmpeg库并创建一个简单的Qt界面,包括按钮和QLabel控件。代码部分主要包括引入必要的FFmpeg和Qt库,通过初始化FFmpeg的网络功能和相关结构(如AVFormatContextAVCodecContextAVFrame等),打开视频文件,查找并解码视频流。然后,将解码后的帧转换为RGB格式,并显示在QLabel上,利用QImageQPixmap进行图像处理,确保视频以原始大小缩放后显示。最后,释放所有分配的资源以避免内存泄漏。

前提:配置QT下ffmpeg的动态或静态库的调用

可参考文章:FFmpeg开发(一)一Qt Creator配置FFmpeg_ffmpeg-4.2.1-win32-dev-CSDN博客

验证环境后可进行正式的代码编写:

首先,建立ui界面,因为首次编码,只需一个按钮及一个QLable控件即可

建立完成ui界面即可进行代码的编写,因为控件的使用较少,故直接提供代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTime>
#include <QIcon>
#include <QPixmap>
#include <QFileDialog>
#include <QMessageBox>
#include <QSlider>
#include <QComboBox>

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavformat/version.h>
#include <libavutil/time.h>
#include <libavutil/mathematics.h>
#include <libavutil/imgutils.h>
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug("-----{version}----");
    qDebug("%d", avcodec_version());
}

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

// 延时函数
void delay(int msec)
{
    QTime dieTime = QTime::currentTime().addMSecs(msec);
    while (QTime::currentTime() < dieTime)
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

void MainWindow::on_open_button_clicked()
{
    unsigned char* buf;
    int isVideo = -1;
    int ret;
    unsigned int i, streamIndex = 0;
    const AVCodec *pCodec;
    AVPacket *pAVpkt;
    AVCodecContext *pAVctx;
    AVFrame *pAVframe, *pAVframeRGB;
    AVFormatContext* pFormatCtx;
    struct SwsContext* pSwsCtx;

    avformat_network_init();
    //ffmpeg4.0之后了av移除_register_all();等函数,无需再进行初始化

     char videoPath[] = "test.mov";//"juren-30s.mp4";
    //测试视频需要放在debug目录下,否则难以识别
    //char videoPath[] ="rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";
    //视频流暂时无法播放,后续优化
    // 创建AVFormatContext
    pFormatCtx = avformat_alloc_context();

    // 初始化pFormatCtx
    if (avformat_open_input(&pFormatCtx, videoPath, 0, 0) != 0)
    {
        qDebug("avformat_open_input err.");
        avformat_free_context(pFormatCtx); // 释放资源
        return;
    }

    // 获取音视频流数据信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        avformat_close_input(&pFormatCtx);
        qDebug("avformat_find_stream_info err.");
        return;
    }

    // 找到视频流的索引
    for (i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            streamIndex = i;
            isVideo = 0;
            break;
        }
    }

    // 没有视频流就退出
    if (isVideo == -1)
    {
        avformat_close_input(&pFormatCtx);
        qDebug("nb_streams err.");
        return;
    }

    // 获取视频流编码
    pAVctx = avcodec_alloc_context3(NULL);

    // 查找解码器
    avcodec_parameters_to_context(pAVctx, pFormatCtx->streams[streamIndex]->codecpar);
    //ffmpeg新版中不支持原来的codec,故上文替换为codecper
    pCodec = avcodec_find_decoder(pAVctx->codec_id);
    if (pCodec == NULL)
    {
        avcodec_free_context(&pAVctx); // 释放资源
        avformat_close_input(&pFormatCtx);
        qDebug("avcodec_find_decoder err.");
        return;
    }

    // 初始化pAVctx
    if (avcodec_open2(pAVctx, pCodec, NULL) < 0)
    {
        avcodec_free_context(&pAVctx); // 释放资源
        avformat_close_input(&pFormatCtx);
        qDebug("avcodec_open2 err.");
        return;
    }

    // 初始化pAVpkt
    pAVpkt = av_packet_alloc();
    if (!pAVpkt)
    {
        avcodec_free_context(&pAVctx); // 释放资源
        avformat_close_input(&pFormatCtx);
        qDebug("av_packet_alloc err.");
        return;
    }

    // 初始化数据帧空间
    pAVframe = av_frame_alloc();
    pAVframeRGB = av_frame_alloc();
    if (!pAVframe || !pAVframeRGB)
    {
        av_packet_free(&pAVpkt); // 释放资源
        avcodec_free_context(&pAVctx);
        avformat_close_input(&pFormatCtx);
        qDebug("av_frame_alloc err.");
        return;
    }

    // 创建图像数据存储buf
    buf = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1));
    av_image_fill_arrays(pAVframeRGB->data, pAVframeRGB->linesize, buf, AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1);

    // 根据视频宽高重新调整窗口大小 视频宽高 pAVctx->width, pAVctx->height
    ui->video_window->setGeometry(0, 0, pAVctx->width, pAVctx->height);
    ui->video_window->setScaledContents(true);  // 设置 QLabel 内容缩放

    // 初始化pSwsCtx
    pSwsCtx = sws_getContext(pAVctx->width, pAVctx->height, pAVctx->pix_fmt, pAVctx->width, pAVctx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);

    // 循环读取视频数据
    int mVideoPlaySta = 1;
    while (true)
    {
        if (mVideoPlaySta == 1) // 正在播放
        {
            if (av_read_frame(pFormatCtx, pAVpkt) >= 0) // 读取一帧未解码的数据
            {
                // 如果是视频数据
                if (pAVpkt->stream_index == (int)streamIndex)
                {
                    // 解码一帧视频数据 旧版解码在新版中不适用,故使用
                    //avcodec_send_packet,avcodec_receive_frame代替,希望大家能够熟悉使用
                    ret = avcodec_send_packet(pAVctx, pAVpkt);
                    if (ret < 0)
                    {
                        qDebug("Decode Error: avcodec_send_packet");
                        av_packet_unref(pAVpkt);
                        continue;
                    }

                    ret = avcodec_receive_frame(pAVctx, pAVframe);
                    if (ret == 0)
                    {
                        sws_scale(pSwsCtx, (const unsigned char* const*)pAVframe->data, pAVframe->linesize, 0, pAVctx->height, pAVframeRGB->data, pAVframeRGB->linesize);
                        QImage img((uchar*)pAVframeRGB->data[0], pAVctx->width, pAVctx->height, QImage::Format_RGB32);
                        ui->video_window->setPixmap(QPixmap::fromImage(img));
                        delay(1); // 添加延时
                    }
                    else if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
                    {
                        qDebug("Decode Error: avcodec_receive_frame");
                    }
                }
                av_packet_unref(pAVpkt);
            }
            else
            {
                break;
            }
        }
        else // 暂停
        {
            delay(300);
        }
    }

    // 释放资源
    sws_freeContext(pSwsCtx);
    av_frame_free(&pAVframeRGB);
    av_frame_free(&pAVframe);
    av_packet_free(&pAVpkt);
    avcodec_free_context(&pAVctx);
    avformat_close_input(&pFormatCtx);
    qDebug() << "play finish!";
}

需要注意的点在代码注释中已说明,希望大家能够指正错误!

效果展示

欢迎指正!

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Qt一个功能强大的C++跨平台应用程序开发框架,可以用于开发各种类型的应用程序,包括视频播放器ffmpeg一个开源的音视频解码库,可以对各种格式的音视频文件进行解码和编码。在Qt中,可以使用ffmpeg来实现视频播放功能。 要循环播放一个视频,需要在程序中实现如下步骤: 1. 使用Qt中的QMediaPlayer类来实现视频播放功能。QMediaPlayer类提供了很多方法来控制视频的播放、暂停、停止等功能。 2. 使用QFileDialog类来打开视频文件。QFileDialog类是Qt中提供的一个对话框类,可以用来选择文件和目录。 3. 在QMediaPlayer类中设置循环播放模式。可以使用setMedia或setPlaylist方法来设置视频文件或播放列表,并使用setPlaybackMode方法来设置循环播放模式。 4. 在程序中实现播放完成后的回调函数。可以使用QMediaPlayer类的signals和slots机制来连接视频播放完成信号和处理函数。在处理函数中,通过设置QMediaPlayer类的状态为Stopped状态,然后重新播放视频来实现循环播放。 5. 在Qt应用程序中实现一个主循环,用于处理事件和消息。可以使用QApplication类的exec方法来启动主循环。 6. 最后,编译和运行程序,在程序界面中选择要播放的视频文件,程序将会自动循环播放该视频。 ### 回答2: 要循环播放一个视频,可以使用QtFFmpeg库来实现。 首先,确保你已经成功地集成了QtFFmpeg库到你的项目中。 接下来,需要创建一个包含视频播放功能的Qt窗口。你可以使用Qt的视频播放器组件QMediaPlayer,或者使用FFmpeg提供的接口来实现自定义的播放器。 在窗口类的构造函数中,初始化视频播放器,并设置视频文件的路径。例如,使用QMediaPlayer的setMedia()函数来指定视频文件路径。 然后,将播放器与窗口中的视频显示区域关联起来。使用QMediaPlayer的setVideoOutput()函数,并传入一个用于显示视频的QWidget作为参数。 接下来,在Qt窗口的槽函数中,使用QMediaPlayer的play()函数来开始播放视频。 为了实现循环播放,可以在QMediaPlayer的信号finished()中重新播放视频。在finished()信号的槽函数中,调用QMediaPlayer的setPosition()函数,将视频播放位置重新设置为起始位置,然后再次调用play()函数开始重新播放。 最后,在窗口类的析构函数中,记得释放视频播放器的资源,例如调用QMediaPlayer的stop()函数停止播放,并释放关联的资源。 通过以上步骤,你就可以使用QtFFmpeg来实现循环播放一个视频了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值