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

news/2024/7/10 19:46:00 标签: qt, ffmpeg, 音视频

首先一个完整的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上下文。


    //申请AVCodecContext
    AVCodecContext* 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);
	}

	//申请AVCodecContext
	AVCodecContext* 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.niftyadmin.cn/n/5426658.html

相关文章

关于this指向和react vue2 3 的diff--后续补充

this指向 全局作用域中或者普通函数中this指向全局对象window 立即执行函数this必定指向window 定时器this指向window 事件中this指向事件源对象 方法中谁调用就指向谁 对象内调用方法&#xff0c;谁调用就指向谁 构造函数中this指向对象实例 1.全局作用下默认绑定wind…

逻辑斯特 + 神经网络梯度下降公式推导 + 向量化

全部推导来自吴恩达老师的视频课&#xff0c;下面仅作整理 逻辑斯特 神经网络

opencv dnn模块 示例(25) 目标检测 object_detection 之 yolov9

文章目录 1、YOLOv9 介绍2、测试2.1、官方Python测试2.1.1、正确的脚本2.2、Opencv dnn测试2.2.1、导出onnx模型2.2.2、c测试代码 2.3、测试统计 3、自定义数据及训练3.1、准备工作3.2、训练3.3、模型重参数化 1、YOLOv9 介绍 YOLOv9 是 YOLOv7 研究团队推出的最新目标检测网络…

LM2903BIDR比较器芯片中文资料规格书PDF数据手册参数引脚图功能封装尺寸图

产品概述&#xff1a; M393B 和 LM2903B 器件是业界通用 LM393 和 LM2903 比较器系列的下一代版本。下一代 B 版本比较器具有更低的失调电压、更高的电源电压能力、更低的电源电流、更低的输入偏置电流和更低的传播延迟&#xff0c;并通过专用 ESD 钳位提高了 2kV ESD 性能和输…

16、技巧之九: 修改参数,如何让表格翻页滚动到底部?【Selenium+Python3网页自动化总结】

1、问题提出 在网页配置参数时&#xff0c;输入参数名称搜索&#xff0c;搜出来的同名参数结果有多个&#xff0c;分布在一个表格的不同行&#xff0c;表格是动态加载的&#xff0c;需要滚动鼠标才能把所出参数找出来。用selenium怎么实现这种参数修改&#xff1f; 2、网页元素…

React——关于react概述

react 介绍 React 是一个用于构建用户界面的 JavaScript 库 &#xff08;类UI HTML MVC里的V) react官网(https://reactjs.org/) react中文网(https://zh-hans.reactjs.org/) react三大特点 声明式UI &#xff08;与vue一样&#xff09; 通过数据驱动视图的变化&#xff0c…

WPF —— Calendar日历控件详解

1&#xff1a; Calendar的简介 日历控件用于创建可视日历&#xff0c;让用户选择日期并在选择日期时触发事件。 DisplayMode 用来调整日历显示模式&#xff0c;分为Month、Year 和Decade 三种。如下是None 2&#xff1a;Calendar控件常用的属性 SelectionMode 选中日历的类…

python之自动化(django)

1、安装 我用的是pip install Django 在命令行中安装 然后django-admin startproject autotext&#xff08;在命令行中&#xff09; 这句话是创建一个django 项目 然后切换到你所创建项目的目录下 输入&#xff1a; python manage.py runserver 当你出现以下错误时 You…