Qt+FFmpeg+opengl从零制作视频播放器-4.音频解码

首先一个完整的MP4文件解封装之后,得到了压缩的音频数据,这个数据是不能直接拿去播放的,我们需要解码成原始的PCM数据才能够播放,解码音频数据,如下图所示,把MP3或者AAC数据解码成原始的数据pcm。

音频解码是将编码的音频数据(如MP3, AAC, OGG等格式)转换为可以播放的PCM(脉冲编码调制)数据的过程。这个过程通常涉及以下步骤:

  • 解封装(Demuxing):

    • 从容器格式(如MP4, MKV, AVI等)中分离出音频数据流。
    • 读取音频流的元数据,包括编码类型、采样率、通道数、比特率等。
  • 解码准备:

    • 初始化解码器。找到与音频流匹配的解码器(例如:libmp3lame解码MP3数据流)。
    • 打开解码器,准备开始解码。
  • 循环解码:

    • 从分离出的音频数据流中读取编码的音频数据包(packet)。
    • 将编码的数据包发送到解码器进行解码。
    • 从解码器中接收解码后的帧数据(解码器可能需要多个数据包才能生成一个完整的帧)。
  • 帧处理:

    • 将解码出来的帧(PCM数据)进行可能的后处理,例如重采样(如果需要改变采样率)、声道转换(比如立体声到单声道)、音量调整等。
    • 处理后的帧数据准备播放或进一步处理。
  • 同步和播放:

    • 如果需要与视频同步,采取相应的机制确保音频和视频能够同步播出。
    • 将解码、处理后的音频数据送至音频输出设备播放。
  • 流的结束:

    • 处理音频流的结束,这可能涉及刷新解码器以输出最后几帧,关闭解码器和清理资源。

ffmpeg解码音频的数据步骤:首先前面的解封装步骤不能少。

Qt+FFmpeg+opengl从零制作视频播放器-3.解封装

查找解码器,根据音频流的codec_id找到解码器。

//找到解码器AVCodec *codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec){fprintf(stderr, "Codec not found\n");exit(1);}

申请AVCodecContenxt上下文。

//申请AVCodecContextAVCodecContext* m_pCodecCtx= nullptr;m_pCodecCtx = avcodec_alloc_context3(codec);if (!codec_ctx){exit(1);}

配置解码器上下文参数。

	///配置解码器上下文参数avcodec_parameters_to_context(m_pCodecCtx, para);

打开解码器。

	int ret = avcodec_open2(m_pCodecCtx, 0, 0);if (ret != 0){avcodec_free_context(&m_pCodecCtx);cout << "avcodec_open2  failed! :" << buf << endl;}

然后通过while循环,不停的读取数据,解码。

av_read_frame(inputFmtCtx, pkt)avcodec_send_packet(m_pCodecCtx, pkt);avcodec_receive_frame(m_pCodecCtx, frame);

执行结束后关闭输入文件,释放资源。

    //关闭avformat_close_input(&inputFmtCtx);//释放avformat_free_context(inputFmtCtx);//释放资源av_packet_free(&pkt);

AVCodecParameters 用于保存音视频流的基本参数信息,音频相关的成员变量解析:

  • codec_type:这是一个枚举类型AVMediaType,用于指定编解码器的类型。对于音频来说,可以是AVMEDIA_TYPE_AUDIO,表示音频数据。
  • codec_id:这个枚举类型的成员变量指定了编码格式,例如MP3、AAC等音频编码格式。
  • format:这个成员变量对于音频来说指的是采样格式,如16位PCM、32位浮点等。
  • channels:这个成员变量表示音频的通道数,即单声道、立体声或多声道等。
  • sample_rate:这个成员变量表示音频的采样率,即每秒钟采样的次数,通常以Hz为单位。
  • channel_layout:这个成员变量指定了音频通道的布局,如立体声、环绕声等。
        int ret = avformat_open_input(&m_pFormatCtx, url, NULL, &opts);.........m_sampleRate = m_pFormatCtx->streams[m_audioIndex]->codec->sample_rate;m_channels = m_pFormatCtx->streams[m_audioIndex]->codec->channels;m_aTimeBase = r2d(m_pFormatCtx->streams[m_audioIndex]->time_base);cout << "=======================================================" << endl;cout << m_audioIndex << " audio info" << endl;cout << "codec_id = " << m_pFormatCtx->streams[m_audioIndex]->codecpar->codec_id << endl;cout << "format = " << m_pFormatCtx->streams[m_audioIndex]->codecpar->format << endl;cout << "sample_rate = " << m_sampleRate << endl;cout << "channels = " << m_channels << endl;cout << "=======================================================" << endl;

源码示例:保存音频前200帧数据。

#include <QtCore/QCoreApplication>extern "C"
{
#include "libavformat/avformat.h"
#include "libavutil/dict.h"
#include "libavutil/opt.h"
#include "libavutil/timestamp.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h" 
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//av_register_all();avformat_network_init();AVFormatContext* ifmt_ctx = NULL;const char* inputUrl = "F:/1920x1080.mp4";///打开输入的流int ret = avformat_open_input(&ifmt_ctx, inputUrl, NULL, NULL);if (ret != 0){printf("Couldn't open input stream.\n");return -1;}//查找流信息if (avformat_find_stream_info(ifmt_ctx, NULL) < 0){printf("Couldn't find stream information.\n");return -1;}//找到音频流索引int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);AVStream* st = ifmt_ctx->streams[audio_index];AVCodec* codec = nullptr;//找到解码器codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec){fprintf(stderr, "Codec not found\n");exit(1);}//申请AVCodecContextAVCodecContext* codec_ctx = nullptr;codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx){exit(1);}avcodec_parameters_to_context(codec_ctx, ifmt_ctx->streams[audio_index]->codecpar);//打开解码器if ((ret = avcodec_open2(codec_ctx, codec, NULL) < 0)){return -1;}AVPacket* pkt = av_packet_alloc();//av_init_packet(pkt);AVFrame *frame = av_frame_alloc();char fileName[20] = "test.pcm";//保存pcm文件FILE* f;f = fopen(fileName, "wb");static int frameCount = 0;//不断读取数据while (av_read_frame(ifmt_ctx, pkt) >= 0){if (pkt->stream_index == audio_index){int ret = avcodec_send_packet(codec_ctx, pkt);if (ret >= 0){ret = avcodec_receive_frame(codec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){continue;}else if (ret < 0){continue;}//保存200帧数据if(frameCount >= 200)break;//获取数据大小int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt);if (data_size < 0) {continue;}for (int i = 0; i < frame->nb_samples; i++){for (int ch = 0; ch < codec_ctx->channels; ch++){fwrite(frame->data[ch] + data_size * i, 1, data_size, f);}}}frameCount++;}}fclose(f);printf("write finished\n");avcodec_close(codec_ctx);avcodec_free_context(&codec_ctx);avformat_close_input(&ifmt_ctx);av_frame_free(&frame);av_packet_free(&pkt);return a.exec();
}

使用pcm数据工具,用于播放pcm文件。

pcm工具pcm工具pcm工具-C++文档类资源-CSDN下载

使用pcm工具播放 保存好的pcm文件。

选择导入原始数据,点击Detect按钮,自动获取pcm的格式。

FFmpeg是一个多功能的多媒体处理工具,它用于转换、编码、解码、转码等多种任务。以下是一些常用的FFmpeg命令:

获取视频信息:`ffmpeg -i [输入文件名]`来获取视频的详细信息。
视频格式转换:例如,将MP4格式的视频转换为FLV格式,可以使用命令`ffmpeg -i input.mp4 -f flv output.flv`。
音频格式转换:将MP3格式的音频转换为PCM格式,可以使用命令`ffmpeg -i input.mp3 -f s16be -ar 16000 -ac 1 -acodec pcm_s16be output.pcm`。
音视频分离:使用命令`ffmpeg -i input.mp4 -vcodec copy -an output.mp4`来去除音频,只保留视频。
截取视频:使用命令`ffmpeg -i input.mp4 -ss 8 -t 2 -s 1280x720 -codec copy -f flv output.flv`来截取一段视频。
音视频同步:使用命令`ffmpeg -i input.mp4 -ss 8 -t 2 -i input.aac -c copy -map 0:v:0 -map 1:a:0 output.mp4`来将视频和音频同步。
音视频编码:使用命令`ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast -crf 22 -c:a aac -b:a 128k output.mp4`来对视频和音频进行编码。

完整工程: 

https://download.csdn.net/download/wzz953200463/88959152icon-default.png?t=N7T8https://download.csdn.net/download/wzz953200463/88959152

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

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

相关文章

【BFS二叉树】113路径总和II

113路径总和 II 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 思路&#xff1a; 题目最终输出的是路径&#xff0c;因此用BFS遍历的时候&#xff0c;需要记录走到每个节点的路径&#xff1…

(done) 使用 vscode 快速把 .ipynb 文件 转为 .py 文件

如图&#xff0c;先点红圈 随后点 Export 后续操作凭直觉 可以使用 vim 的匹配功能把那些难看的符号给删掉&#xff0c;比如 :%s/# \%\%.*$//g

git区域与对象

大纲 工作区(workspace directory):本机的代码项目,是一种沙箱环境 暂存区(stage index):工作区在程序员写程序的过程中会发生无数次改动&#xff0c;git不可能记录每一次的改动&#xff0c;这些改动的过程在暂存区负责记录&#xff0c;暂存区会将最终的状态随着程序员的提交…

ChromeDriver 122 版本为例 国内下载地址及安装教程

ChromeDriver 国内下载地址 https://chromedriver.com/download 靠谱 千千万万别下载错了 先确认 Chrome 浏览器版本 以 win64 版本为例 那我们下载这一个啊&#xff0c;不要下载错了 下载地址贴在这哈 https://storage.googleapis.com/chrome-for-testing-public/122.0.…

vscode 之 vue项目如何使用ctrl+鼠标左键跳转对应文件

话不多说&#xff0c;直接步入正题 使用项目工程的jsconfig.json 直接在项目中使用jsconfig.json/tsconfig.json文件配置&#xff08;项目中没有相应的文件的添加文件&#xff0c;有文件的添加"paths": {"/*": ["./src/*"]}配置即可&#xff0…

javaweb day16 mysql

mysql 安装&#xff1a; 企业开发使用方法 安装虚拟机代替服务器 数据模型 创建数据库 写法 sql简介

如何用 Vue3 + Vite + SCSS 轻松实现换肤功能

前言 一个网站的换肤效果算是一个比较常见的功能&#xff0c;尤其是在后台管理系统中&#xff0c;我们几乎都能看到他的身影&#xff0c;这里给大家提供一个实现思路。 搭建项目 vitevue3搭建项目这里就不演示了&#xff0c;vite官网里面讲得很清楚。 注&#xff1a;这里使…

EdgeSAM: Prompt-In-the-Loop Distillation for On-Device Deployment of SAM

EdgeSAM: Prompt-In-the-Loop Distillation for On-Device Deployment of SAM EdgeSAM论文&#xff1a;https://arxiv.org/pdf/2312.06660.pdf EdgeSAM代码&#xff1a;https://github.com/chongzhou96/EdgeSAM 1 概述 作者在对各种蒸馏策略进行深入剖析后&#xff0c;证实了…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:TextClock)

TextClock组件通过文本将当前系统时间显示在设备上。支持不同时区的时间显示&#xff0c;最高精度到秒级。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 TextClock(options?…

Redis底层数据结构之List

文章目录 1. Redis 6的list源码分析1. Redis 7的list源码分析 1. Redis 6的list源码分析 首先我们查看一下redis 6关于list的相关配置&#xff1a; config get list*可以看见redis 6的quicklist底层使用的数据结构是ziplist list-compress-depth&#xff1a;表示一个quicklis…

CSS中position的属性有哪些,区别是什么

position有以下属性值&#xff1a; 属性值概述absolute生成绝对定位的元素&#xff0c;相对于static定位以外的一个父元素进行定位。元素的位置通过left、top、right、bottom属性进行规定。relative生成相对定位的元素&#xff0c;相对于其原来的位置进行定位。元素的位置通过…

使用Golong轻松实现JWT身份验证

使用Golong轻松实现JWT身份验证 JSON Web Tokens (JWT)是一种流行的安全方法&#xff0c;用于在两个方之间表示声明。在Web应用程序领域&#xff0c;它们通常用作从客户端向服务器传输身份信息&#xff08;声明&#xff09;的方式。本教程将引导您逐步实现Go应用程序中的JWT身份…