当前位置: 首页 > news >正文

【Qt+FFMPEG】 - 封装 解码音视频 线程

目录

一:界面显示与解码的同步进行--线程

二:测试

三:因为路径问题导致的图片不显示

四:QString字符串分割函数---获取文件名称

一:界面显示与解码的同步进行--线程

解码利用线程的方式进行改造

videodecode.h .cpp

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

#ifndef VIDEODECODE_H
#define VIDEODECODE_H
 
#include <QObject>
#include<QThread>//使用线程
 
//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
//avformat:封装格式处理
#include <libavformat/avformat.h>
//swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
//avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
//avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
}
 
class videoDecodeThread : public QThread//利用线程基类
{
    Q_OBJECT
public:
    //    explicit videoDecodeThread(QObject *parent = 0);
    videoDecodeThread(QString videoname);
    QString videoname;
 
    //视频文件上下文格式
    AVFormatContext* avformat_context;
    //编解码器上下文格式
    AVCodecContext* avcodec_context;
    //解码器上下文格式
    AVCodec* avcodec;
 
    AVPacket* av_packet;
    //流的标志位
    int av_stream_index;
 
    //线程函数的引入
    void run();
    virtual ~videoDecodeThread();
    //定义停止的标志位
    bool m_stop;
 
 
 
signals:
 
public slots:
};
 
#endif // VIDEODECODE_H
#include "videodecode.h"
#include<QDebug>
#include<QCoreApplication>
#include<QThread>
 
//解码初始化操作
//1.注册所有组件
//2.打开视频输入文件
//3.查找视频流信息
//4.查找解码器
//5.打开解码器
 
//videoDecode::videoDecode(QObject *parent) : QObject(parent)
//{
//}
 
videoDecodeThread::videoDecodeThread(QString videoname)
{
    //初始线程未启动
    this->m_stop = false;
    qDebug()<<"1.注册所有组件";
    av_register_all();
 
    qDebug()<<"2.打开视频输入文件";
    QString filename = QCoreApplication::applicationDirPath();
    qDebug()<<"获取程序运行目录 "<<filename;
    QString cinputFilePath = "test.avi";  //本地视频文件放入程序运行目录
    avformat_context = avformat_alloc_context();
    //参数一:封装格式上下文->AVFormatContext->包含了视频信息(视频格式、大小等等...)
    //参数二:打开文件(入口文件)->url
    qDebug()<<"打开"<<videoname<<"视频文件进行播放";
    int avformat_open_result = avformat_open_input(&avformat_context,videoname.toStdString().c_str(),NULL,NULL);
    if (avformat_open_result != 0)
    {
        //获取异常信息
        char* error_info = new char[32];
        av_strerror(avformat_open_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
 
    };
 
    qDebug()<<"3.查找视频流信息";
    //参数一:封装格式上下文->AVFormatContext
    //参数二:配置
    //返回值:0>=返回OK,否则失败
    int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context, NULL);
    if (avformat_find_stream_info_result < 0){
        //获取失败
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }
 
    qDebug()<<"4.查找解码器";
    //第一点:获取当前解码器是属于什么类型解码器->找到了视频流
    //音频解码器、视频解码器、字幕解码器等等...
    //获取视频解码器流引用
    av_stream_index = -1;
    for (int i = 0; i < avformat_context->nb_streams; ++i) {
        //循环遍历每一流
        //视频流、音频流、字幕流等等...
        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            //找到了
            av_stream_index = i;
            break;
        }
    }
    if (av_stream_index == -1)
    {
        qDebug()<<QString("没有找到视频流");
    }
    //第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
    //编解码器上下文
    avcodec_context = avformat_context->streams[av_stream_index]->codec;
 
    //第三点:根据解码器上下文->获取解码器ID
    avcodec = avcodec_find_decoder(avcodec_context->codec_id);
    if (avcodec == NULL)
    {
        qDebug()<<QString("没有找到视频解码器");
    }
 
    qDebug()<<"5.打开解码器";
    int avcodec_open2_result = avcodec_open2(avcodec_context,avcodec,NULL);
    if (avcodec_open2_result != 0)
    {
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }
 
    qDebug()<<"视频详细信息输出";
    //此函数自动打印输入或输出的详细信息
    av_dump_format(avformat_context, 0, cinputFilePath.toStdString().c_str(), 0);
    qDebug()<<"----------------解码准备工作完成-----------------";
 
}
 
void videoDecodeThread::run()
{
    qDebug()<<"----------------开始循环解码操作-----------------";
 
    qDebug()<<"6.循环解码";
 
    //读取帧数据换成到哪里->缓存到packet里面
    av_packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    //解码的状态类型(0:表示解码完毕,非0:表示正在解码)
    int current_frame_index = 0;
 
    //>=0:说明有数据,继续读取   <0:说明读取完毕,结束
    //从视频文件上下文中读取包--- 有数据就一直读取
    while (av_read_frame(avformat_context,av_packet) >= 0)
    {
        //解码什么类型流(视频流、音频流、字幕流等等...)
        if (av_packet->stream_index == av_stream_index)
        {
            //遍历每一帧的信息进行打印
            current_frame_index++;
            //延时操作  1秒显示25帧--1000/25=40
            QThread::msleep(40);
            qDebug()<<QString("当前遍历第 %1 帧").arg(current_frame_index);
        }
    }
    qDebug()<<"7.关闭所有解码组件";
    av_packet_free(&av_packet);
    //关闭流
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);
 
}
 
videoDecodeThread::~videoDecodeThread()
{
    qDebug()<<"7.关闭所有解码组件";
    av_packet_free(&av_packet);
    //关闭流
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);
 
}

二:测试 

    if(btnName == "儿童")
    {
        QMessageBox::question(NULL,"儿童",btn->text());
 
        QString cinputFilePath = "test.avi";
        videoDecodeThread *p = new videoDecodeThread(cinputFilePath);
        p->start();
 
    }

 

 

点击其他的按钮,找到对应的item项下的视频进行播放 

三:因为路径问题导致的图片不显示

    QString videopath = QDir::currentPath()+"/image";
    qDebug()<<videopath;
    QDir dir(videopath);//确定路径 从此dir路径下往下找

将image文件夹拷贝到bin文件目录下 

 

图片即可以正常显示

视频播放器中的视频图片尽量和片源名字保持一致

四:QString字符串分割函数---获取文件名称 

void IndexWin::videoWidgetShowSlot(QListWidgetItem *item)
{
    qDebug()<<item->text()<<"界面创建";
 
    QStringList list = item->text().split(".");//QString字符串分割函数
    qDebug()<<list[0];
    //视频文件名称后缀:.avi
    QString cinputFilePath = list[0]+".avi";
 
//  解码线程的使用
    videoDecodeThread *p = new videoDecodeThread(cinputFilePath);
    //新建视频播放界面
 
//    通过设置按钮进行视频的播放
//    p->start();
 
}

注意点:视频文件名称后缀为.avi

 

若是没有根据工程目录下视频文件后缀名称命名---会导致程序的异常结束

    //视频文件名称后缀:.avi
    QString cinputFilePath = list[0]+".avi";

点击列表中的item进行解码操作---模拟视频播放器

 

 如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

相关文章:

  • Arduino框架下最便宜的开发芯片-CH552初探
  • Java高并发编程实战5,异步注解@Async自定义线程池
  • 前端进阶——ES6
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • 【Python+大数据】第一天:安装VMware及Centos,配置虚拟机网络,学习Linux命令。研究生开学10天的感受。
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • JAVA代码操作HDFS
  • web前端开发基础教程一
  • 原子尺度仿真对材料设计效率的提升,是未来材料研发的关键核心竞争力
  • CDH 10Cloudera Manager Kerberos安装配置CA配置(markdown新版三)
  • RedHat7无法安装Telnet
  • LeetCode刷题(二):前言
  • 网络套接字实现TCP机制通信
  • 一个非教条式的TDD例子
  • Spring 整合 MyBatis
  • 网络传输文件的问题
  • “大数据应用场景”之隔壁老王(连载四)
  • android 一些 utils
  • CSS中外联样式表代表的含义
  • Hibernate【inverse和cascade属性】知识要点
  • JavaScript服务器推送技术之 WebSocket
  • JS函数式编程 数组部分风格 ES6版
  • js写一个简单的选项卡
  • Logstash 参考指南(目录)
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • Vue.js源码(2):初探List Rendering
  • vue:响应原理
  • 程序员最讨厌的9句话,你可有补充?
  • 当SetTimeout遇到了字符串
  • 关于Flux,Vuex,Redux的思考
  • 关于springcloud Gateway中的限流
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 树莓派 - 使用须知
  • 线性表及其算法(java实现)
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 用jquery写贪吃蛇
  • 原生Ajax
  • 怎么将电脑中的声音录制成WAV格式
  • 通过调用文摘列表API获取文摘
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • (C语言)逆序输出字符串
  • (k8s中)docker netty OOM问题记录
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (二)斐波那契Fabonacci函数
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (七)Knockout 创建自定义绑定
  • (十)c52学习之旅-定时器实验
  • (图)IntelliTrace Tools 跟踪云端程序
  • (一)Linux+Windows下安装ffmpeg
  • (转)机器学习的数学基础(1)--Dirichlet分布