热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【FFmpeg(2016)】SwrContext重采样结构体

【前言】在看这篇文章前,我推荐你先看看我的另外两篇文章:【FFmpeg(2016)】视频文件分离器(demuxing)——H264&PCM【FFmpeg

【前言】


在看这篇文章前,我推荐你先看看我的另外两篇文章:


【FFmpeg(2016)】视频文件分离器(demuxing)——H264&PCM

【FFmpeg(2016)】PCM编码AAC


本文章主要介绍SwrContext的用处 和 使用方法,到底什么是重采样,为什么需要重采样,希望你耐心看完。


重采样,也就是对已得到的数据进行重新的采样;比如,原先的PCM音频数据是2 个声道,44100采样率,32bit 单精度型(sample format采样格式)。


那么通过重采样,我们改变它的声道数、采样率和采样格式。


对于改变声道数 和 采样率 ,在这里不着重探讨,主要理解 sample format.


【FFmpeg数据结构】

官方对sample format进行了定义,如下:


enum AVSampleFormat {
    AV_SAMPLE_FMT_NOnE= -1,
    AV_SAMPLE_FMT_U8,          /// 
 
枚举类型带P的代表非平面类型,带P的代表平面类型。


【FFmpeg平面的概念】


所谓平面与非平面是针对在编解码的时候,数据存储的方式来判定的,那怎么判断呢?


比如,我有一个PCM文件是这样的(L代表左声道,R代表右声道):

L(一个采样点)R(一个采样点)LRLRLRLRLRLRLRLR........................................

注意,是一个采样点,如果音频sample format 是AV_SAMPLE_FMT_FLT,那就说明一个采样点是32位。


我用ffplay播放这个音频时:



图.1


-f f32le 代表这个文件sample format 是32bit 的,也就是AV_SAMPLE_FMT_FLT,

-ar 是采样率

-ac 是声道数


上面说到我这个PCM文件,对于FFmpeg来说,是AV_SAMPLE_FMT_FLT格式的,它的存储方式就是:

L(一个采样点)R(一个采样点)LRLRLRLRLRLRLRLR........................................

也就是说,对于FFmpeg来说,这个PCM文件是非平面的。



【planar“平面”概念】


我以我这篇文章的代码为例:

【FFmpeg(2016)】PCM编码AAC


如果我直接:

FILE *fp = fopen("a.pcm", "rb");
fread(....);

从L(一个采样点)R(一个采样点)LRLRLRLRLRLRLRLR.....................格式的PCM文件读取一帧,那么AVFrame结构体的data成员状态如下:data是存储着准备要被编码的音频数据,data是一个二维指针


图.2

如果直接对这样的data数组进行AAC编码,你会发现得到的AAC文件播放完成变声了,还很嚓。

到底是什么问题?

这就涉及到planar的概念了

自FFmpeg提及平面概念后,要求的就是每个data指针指向各自声道的数据,也就说,要这样:


图. 3

此时,就是所谓的平面,二维数组的第一维存储了左声道,第二维存储了右声道。

如果对这样的data进行AAC编码,那么得到的数据就是正确的。

这里还有另外一个问题,data里的一个一维数组应该多长,这就要看AVCodecContext的成员frame_size,它代表每读取一帧时读取的采样个数,默认是1024.这种情况下,进行AAC编码前的数据存储情况应该是:



图 .4



【代码】使用SwrContext,输入文件为L(一个采样点)R(一个采样点)LRLRLRLRLRLRLRLR.....................格式的PCM文件

思想是:读入PCM时,data情况是图.2,此时使用SwrContext将其转换成图.4, 然后在进行AAC编码 (注意,图2总共也是8192字节)

extern "C"
{
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavutil/frame.h"
#include "libavutil/samplefmt.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "swscale.lib")
#pragma comment(lib, "swresample.lib")


/* PCM转AAC */
int main()
{
	/* ADTS头 */
	char *padts = (char *)malloc(sizeof(char) * 7);
	int profile = 2;	//AAC LC
	int freqIdx = 4;  //44.1KHz
	int chanCfg = 2;  //MPEG-4 Audio Channel Configuration. 1 Channel front-center,channel_layout.h
	padts[0] = (char)0xFF; // 11111111     = syncword
	padts[1] = (char)0xF1; // 1111 1 00 1  = syncword MPEG-2 Layer CRC
	padts[2] = (char)(((profile - 1) <<6) + (freqIdx <<2) + (chanCfg >> 2));
	padts[6] = (char)0xFC;

	SwrContext *swr_ctx = NULL;
	AVCodecContext *pCodecCtx = NULL;
	AVCodec *pCodec = NULL;
	AVFrame *pFrame;
	AVPacket pkt;
	AVCodecID codec_id = AV_CODEC_ID_AAC;

	FILE *fp_in;
	FILE *fp_out;
	char filename_in[] = "audio.pcm";
	char filename_out[] = "audio.aac";

	uint8_t **convert_data;			//存储转换后的数据,再编码AAC
	int i, ret, got_output;
	uint8_t* frame_buf;
	int size = 0;
	int y_size;
	int framecnt = 0;
	int framenum = 100000;


	avcodec_register_all();

	pCodec = avcodec_find_encoder(codec_id);
	if (!pCodec) {
		printf("Codec not found\n");
		return -1;
	}

	pCodecCtx = avcodec_alloc_context3(pCodec);
	if (!pCodecCtx) {
		printf("Could not allocate video codec context\n");
		return -1;
	}

	pCodecCtx->codec_id = codec_id;
	pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
	pCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
	pCodecCtx->sample_rate = 44100;
	pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
	pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);



	qDebug() <<"pCodecCtx->bit_rate ----------------> " <bit_rate;
	qDebug() <<"pCodecCtx->bit_rate ----------------> " <bit_rate;
	qDebug() <channel_layout);


	if ((ret = avcodec_open2(pCodecCtx, pCodec, NULL)) <0) {
		qDebug() <<"avcodec_open2 error ----> " <nb_samples = pCodecCtx->frame_size;	//1024
	pFrame->format = pCodecCtx->sample_fmt;
	pFrame->channels = 2;
	qDebug() <<"frame_size(set pFrame->nb_samples) -------------> " <frame_size;


	/* 由AV_SAMPLE_FMT_FLT转为AV_SAMPLE_FMT_FLTP */
	swr_ctx = swr_alloc_set_opts(
		NULL,
		av_get_default_channel_layout(pCodecCtx->channels),
		pCodecCtx->sample_fmt,				    //在编码前,我希望的采样格式
		pCodecCtx->sample_rate,
		av_get_default_channel_layout(pCodecCtx->channels),
		AV_SAMPLE_FMT_FLT,					    //PCM源文件的采样格式
		pCodecCtx->sample_rate,
		0, NULL);

	swr_init(swr_ctx);
	/* 分配空间 */
	convert_data = (uint8_t**)calloc(pCodecCtx->channels,
		sizeof(*convert_data));
	av_samples_alloc(convert_data, NULL,
		pCodecCtx->channels, pCodecCtx->frame_size,
		pCodecCtx->sample_fmt, 0);



	size = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 0);
	
	frame_buf = (uint8_t *)av_malloc(size);
	/* 此时data[0],data[1]分别指向frame_buf数组起始、中间地址 */
	ret = avcodec_fill_audio_frame(pFrame, pCodecCtx->channels, pCodecCtx->sample_fmt, (const uint8_t*)frame_buf, size, 0);
	
	if (ret <0)
	{
		qDebug() <<"avcodec_fill_audio_frame error ";
		return 0;
	}

	//Input raw data
	fp_in = fopen(filename_in, "rb");
	if (!fp_in) {
		printf("Could not open %s\n", filename_in);
		return -1;
	}

	//Output bitstream
	fp_out = fopen(filename_out, "wb");
	if (!fp_out) {
		printf("Could not open %s\n", filename_out);
		return -1;
	}

	//Encode
	for (i = 0; i frame_size,
			(const uint8_t**)pFrame->data, pCodecCtx->frame_size);

		/* 将转换后的数据复制给pFrame */
		int length = pCodecCtx->frame_size * av_get_bytes_per_sample(pCodecCtx->sample_fmt);
		for (int k = 0; k <2; ++k)
		for (int j = 0; j data[k][j] = convert_data[k][j];
		}

		pFrame->pts = i;

		qDebug() <<"frame->nb_samples -----> " <nb_samples;
		qDebug() <<"size ------------------> " <linesize[0] ----> " <linesize[0];



		ret = avcodec_encode_audio2(pCodecCtx, &pkt, pFrame, &got_output);

		if (ret <0) {
			qDebug() <<"error encoding";
			return -1;
		}

		if (pkt.data == NULL)
		{
			av_free_packet(&pkt);
			continue;
		}

		qDebug() <<"got_ouput = " < 11));
			padts[4] = (char)(((7 + pkt.size) & 0x7FF) >> 3);
			padts[5] = (char)((((7 + pkt.size) & 7) <<5) + 0x1F);
			fwrite(padts, 7, 1, fp_out);
			fwrite(pkt.data, 1, pkt.size, fp_out);

			av_free_packet(&pkt);
		}
	}
	//Flush Encoder
	for (got_output = 1; got_output; i++) {
		ret = avcodec_encode_audio2(pCodecCtx, &pkt, NULL, &got_output);
		if (ret <0) {
			printf("Error encoding frame\n");
			return -1;
		}
		if (got_output) {
			printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", pkt.size);
			padts[3] = (char)(((chanCfg & 3) <<6) + ((7 + pkt.size) >> 11));
			padts[4] = (char)(((7 + pkt.size) & 0x7FF) >> 3);
			padts[5] = (char)((((7 + pkt.size) & 7) <<5) + 0x1F);

			fwrite(padts, 7, 1, fp_out);
			fwrite(pkt.data, 1, pkt.size, fp_out);
			av_free_packet(&pkt);
		}
	}

	fclose(fp_out);
	avcodec_close(pCodecCtx);
	av_free(pCodecCtx);
	av_freep(&pFrame->data[0]);
	av_frame_free(&pFrame);

	av_freep(&convert_data[0]);	
	free(convert_data);			
	//////////////////////////////////////////////////////////////////////////////////

	return 0;
}


上面swr_convert函数执行后,convert_data情况是图.4


AVFrame情况是图.2,所以接下来我将convert_data直接复制给data。


此时编码出来的AAC就是正确的了。







推荐阅读
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 用pandas库修改excel文件里的内容,并把excel文件格式存为csv格式,再将csv格式改为html格式
    假设有Excel文件data.xlsx,其中内容为:     ID age height    sex weight张三  1  39    181 female     85李四  2  40    180   male     80王五  3  38    178 female     78赵六  4  59    1 ... [详细]
  • 一.avcodec_find_decoder获取解码器。在使用之前必须保证所用到的解码器已经注册,最简单的就是调用avcodec_register_all()函数,就像之前注册 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 本文介绍了在win7电脑上进行文件加密的方法,包括利用NTFS的EFS进行加密和使用Win7旗舰版的Bitlocker加密整个分区。同时推荐了超级加密3000、宏杰加密工具和超级盘加密工具等多种加密软件,这些软件具有快速的加密速度和高强度的加密功能,可以防止文件的删除、复制和移动。此外,还强调了保持加密密钥的重要性,以免重装系统后无法打开已加密的文件。最后,提醒读者选择绿色软件,方便使用。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 在2022年,随着信息化时代的发展,手机市场上出现了越来越多的机型选择。如何挑选一部适合自己的手机成为了许多人的困扰。本文提供了一些配置及性价比较高的手机推荐,并总结了选择手机时需要考虑的因素,如性能、屏幕素质、拍照水平、充电续航、颜值质感等。不同人的需求不同,因此在预算范围内找到适合自己的手机才是最重要的。通过本文的指南和技巧,希望能够帮助读者节省选购手机的时间。 ... [详细]
author-avatar
mobiledu2502856097
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有