FFmpeg 音频解码重采样相关的内容

FFMPEG-数据结构解释(AVCodecContext,AVStream,AVFormatContext)_Howie的专栏-CSDN博客
FFmpeg学习4:音频格式转换_ansu1912的博客-CSDN博客

GitHub - ccj659/NDK-FFmpeg-master: Video and audio decoding based with FFmpeg 基于ffmpeg的 视频解码 音频解码.播放等

AVFormatContext

具体结构体内容参考源码。

          这个结构体描述了一个媒体文件或媒体流的构成和基本信息。在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。下面看几个主要变量的作用(在这里考虑解码的情况):

AVIOContext *pb:输入数据的缓存
unsigned int nb_streams:视音频流的个数
AVStream **streams:视音频流
char filename[1024]:文件名
int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata:元数据

av_malloc():

AVIOContext

AVIOContext的初始化函数是avio_alloc_context(),销毁的时候使用av_free()释放掉其中的缓存即可。

AVIOContext中有以下几个变量比较重要:
unsigned char *buffer:缓存开始位置
int buffer_size:缓存大小(默认32768)
unsigned char *buf_ptr:当前指针读取到的位置
unsigned char *buf_end:缓存结束的位置
void *opaque:URLContext结构体

在解码的情况下,buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候,先把数据从硬盘读入buffer,然后在送给解码器用于解码。

AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence));

avformat_open_input  打开一个文件并解析。可解析的内容包括:视频流、音频流、视频流参数、音频流参数、视频帧索引。在调用该函数之前,须确保av_register_all()和avformat_network_init()已调用。

参数说明:

AVFormatContext **ps, 格式化的上下文。要注意,如果传入的是一个AVFormatContext*的指针,则该空间须自己手动清理,若传入的指针为空,则FFmpeg会内部自己创建。

const char *url, 传入的地址。支持http,RTSP,以及普通的本地文件。地址最终会存入到AVFormatContext结构体当中。

AVInputFormat *fmt, 指定输入的封装格式。一般传NULL,由FFmpeg自行探测。

AVDictionary **options, 其它参数设置。它是一个字典,用于参数传递,不传则写NULL。参见:libavformat/options_table.h,其中包含了它支持的参数设置。

SwrContext *swr_alloc(void);  // 分配重采样的上下文。

SwrContext *swr_alloc_set_opts(struct SwrContext *s, int64_t out_ch_layout, AVSampleFormat out_sample_fmt, int out_sample_rate

, int64_t in_ch_layout, AVSampleFormat in_sample_fmt, int in_sample_rate, int log_offset, void *log_ctx

);

参数1:重采样上下文

参数2:输出的layout, 如:5.1声道…

参数3:输出的样本格式。Float, S16, S24

参数4:输出的样本率。可以不变。

参数5:输入的layout。

参数6:输入的样本格式。

参数7:输入的样本率。

参数8,参数9,日志,不用管,可直接传0

针对音频的播放速度,可以通过样本率的改变而改变。\

swr_convert()

针对每一帧音频的处理。把一帧帧的音频作相应的重采样

int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in, int in_count);

参数1:音频重采样的上下文

参数2:输出的指针。传递的输出的数组

参数3:输出的样本数量,不是字节数。单通道的样本数量。

参数4:输入的数组,AVFrame解码出来的DATA

参数5:输入的单通道的样本数量。

av_read_frame

av_read_frame的作用是读取一帧视频数据或者读取多帧音频数据,读取的数据都是待解码的数据

avcodec_decode_audio4

//#include "stdafx.h"
#include <stdio.h>
//#define __STD_CONSTANT_MACROS

//extern "C"
//{
#include "libavcodec/avcodec.h"     
#include "libavformat/avformat.h"	
#include "libswresample/swresample.h"
//}
#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio

int main()
{
	AVFormatContext *pFormatCtx;
	//char *filepath = "WavinFlag.aac";
	char *filepath = "123la.aac";
	FILE *pFile = fopen("output_file.pcm", "wb");

	av_register_all();    
	//avformat_network_init();

	pFormatCtx = avformat_alloc_context();  
	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) < 0) 
	{
		printf("Can't open the input stream.\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0)     
	{
		printf("Can't find the stream information!\n");
		return -1;
	}

	int i, index_audio = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)      
		{
			index_audio = i;
			break;
		}
	}
	if (index_audio == -1)
	{
		printf("Can't find a video stream;\n");
		return -1;
	}

	AVCodecContext *pCodecCtx = pFormatCtx->streams[index_audio]->codec;
	AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     
	if (pCodec == NULL)
	{
		printf("Can't find a decoder!\n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)   
	{
		printf("Can't open the decoder!\n");
		return -1;
	}
	//Audio out parameter:

	//nb_samples: AAC-1024 MP3-1152
	int out_nb_samples = pCodecCtx->frame_size;
	int64_t out_channel_layout = AV_CH_LAYOUT_MONO;    //double channels  AV_CH_LAYOUT_STEREO
	enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;    //Audio sample formats
	int out_sample_rate = 22050;                          //sample rates

	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);  //channel numbers
	uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	
	//�ض���Ƶ��ʽ�µĻ�������С
	int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
	struct SwrContext *aud_convert_ctx = swr_alloc_set_opts(NULL, out_channel_layout, out_sample_fmt, out_sample_rate,
		av_get_default_channel_layout(pCodecCtx->channels), pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
	swr_init(aud_convert_ctx);   

	AVFrame *pFrame = av_frame_alloc();  
	AVPacket *pkt = (AVPacket *)av_malloc(sizeof(AVPacket));
	av_init_packet(pkt);

	int frame_cnt = 0;
	int get_frame;

	while (av_read_frame(pFormatCtx, pkt) >= 0)
	{
		if (pkt->stream_index == index_audio)
		{

			if (avcodec_decode_audio4(pCodecCtx, pFrame, &get_frame, pkt) < 0)
			{
				printf("Decode Error!\n");
				return -1;
			}
			if (get_frame)
			{
				printf("Decoded frame index: %d\n", frame_cnt);
				swr_convert(aud_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data, pFrame->nb_samples);
				fwrite(out_buffer, 1, out_buffer_size, pFile);
				frame_cnt++;
			}

		}
		av_free_packet(pkt);
	}


	//free
	fclose(pFile);

	swr_free(&aud_convert_ctx);

	av_frame_free(&pFrame);
	av_free(out_buffer);

	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);
	avformat_free_context(pFormatCtx);

	return 0;
}

//头文件的引用
/*
gcc decode_new1.c -lasound -lavformat -lavdevice -lavcodec -lswscale -lswresample -lavutil -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib -o decode1
*/
//公共头文件
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>
//拉流头文件
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/file.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <string.h>
#include <stdarg.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <regex.h> 
//解码头文件
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
//重采样头文件
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"

#define BUFFER_SIZE 2048//接收缓冲区的大小,这里显然给大了
#define AudioFormat SND_PCM_FORMAT_S16_LE  
#define AUDIO_CHANNEL_SET   1  			  
#define AUDIO_RATE_SET 22050
//参数声明
//通用参数
int count_test=0;
int err=0,ret=0,i=0;//全局错误返回值保存
//两个小函数的参数
int run_flag=0;//程序运行状态标志位

//文件接口
FILE *file_s16,*file_la;
//拉流全局变量
int pull_flag=0;  //拉流标志位,UDP建立连接后该为置1,代表连接上了
char buffer_aac_la[BUFFER_SIZE]={0};//接收拉流缓冲区
//char buffer_aac_la2[BUFFER_SIZE]={0};
int nPortA =5003;   //端口号
int nRecEcho=0;//实际接收到的字符串长度



//播放参数
snd_pcm_format_t format=AudioFormat;
char buffer_pcm_s16[2048]={0};
snd_pcm_t *capture_handle;// 一个指向PCM设备的句柄
snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置
int buffer_frames =BUFFER_SIZE/2;
unsigned int rate = AUDIO_RATE_SET;

int frame_byte=0;
//信号处理函数
void exit_sighandler(int sig)
{
	run_flag=1;
}
void Exit()
{
    if(run_flag)
    {
        printf("程序终止.\n");
        exit(0);
    }  
}
//拉流函数
void ReceiveRtp()
{
    int socka=0;    //socket描述符,类似于文件描述符 
    struct sockaddr_in addr; // 要绑定给Socka的协议地址 包括族 端口号 地址
    int nRecLen; // 客户端地址长度!!!!!!   
    struct sockaddr_in cli;    // 客户端地址  
    int nRet; // select返回值  
// 创建数据报socka  UDP连接  把地址和端口号给socka
    socka = socket(AF_INET, SOCK_DGRAM, 0); 
    if (socka == -1)  
    {  
        pull_flag=-1;
        printf("创建Socket失败\n");  
        exit(1); 
    }  

    memset(&addr, 0, sizeof(addr));  
    addr.sin_family = AF_INET;   // IPV4协议  
    addr.sin_port = htons(nPortA); // 端口  "10.46.169.189"
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 在本机的所有ip上开始监听addr.sin_addr.s_addr = htonl(INADDR_ANY)
//绑定socket   
    if (bind(socka, (void*)&addr, sizeof(addr)) == -1)
    {  
		pull_flag=-1;
        printf("绑定Socket失败\n");  
        exit(1);    
    }   

	nRecLen = sizeof(cli);
//用到实际返回值
	nRecEcho = recvfrom(socka, buffer_aac_la, BUFFER_SIZE, 0, (struct sockaddr*)&cli, &nRecLen);
	//printf("写了一帧aac数据:%d\n",nRecEcho);
	if (nRecEcho == -1)  
    {  
		pull_flag=-1;
    	printf("recvfrom()\n");  
        exit(1);
    }
	else
		pull_flag=1;	
    fwrite(buffer_aac_la,1,nRecEcho,file_la);//正常
	//printf("拉流成功\n");
//	memcpy(buffer_aac_la,buffer_aac_la2,nRecEcho);
	close(socka);  
}
int read_buffer(void *opaque,uint8_t *buf,int buf_size)
{
	//int true_size=
	memcpy(buf,buffer_aac_la,nRecEcho);
	return nRecEcho;
}
int Decode_OK()
{
    AVFormatContext *pFormatCtx;
	
	unsigned char *aviobuffer=(unsigned char *)av_malloc(nRecEcho);
	AVIOContext *avio=avio_alloc_context(aviobuffer,nRecEcho,0,NULL,read_buffer,NULL,NULL);
	av_register_all();    //注册所有组件
	pFormatCtx = avformat_alloc_context();   //开辟内存
	pFormatCtx->pb=avio;
	if (avformat_open_input(&pFormatCtx, NULL, NULL, NULL) < 0) //打开输入文件
	{
		printf("Can't open the input stream.\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0)     //判断文件流,视频流还是音频流
	{
		printf("Can't find the stream information!\n");
		return -1;
	}

	int i, index_audio = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)      //如果是音频流,则记录存储。
		{
			index_audio = i;
			break;
		}
	}
	if (index_audio == -1)
	{
		printf("Can't find a video stream;\n");
		return -1;
	}

	AVCodecContext *pCodecCtx = pFormatCtx->streams[index_audio]->codec;

	AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     //查找解码器
	if (pCodec == NULL)
	{
		printf("Can't find a decoder!\n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)   //打开编码器
	{
		printf("Can't open the decoder!\n");
		return -1;
	}
	//Audio out parameter:

	//nb_samples: AAC-1024 MP3-1152
	int out_nb_samples = pCodecCtx->frame_size;
	int64_t out_channel_layout = AV_CH_LAYOUT_MONO;    //单   double channels  STEREO
	enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;    //Audio sample formats
	int out_sample_rate = 22050;                          //sample rates

	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);  //channel numbers
	printf("out_channels=%d",out_channels);
	uint8_t *out_buffer = (uint8_t *)av_malloc(1024 * 2);
	//uint8_t *out_buffer = (uint8_t *)av_malloc(1024 * 2);
	//特定音频格式下的缓冲区大小
	int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);

	//因为解码的数据类型是32,播放器播放的是16位需要重采样      pCodecCtx->channel_layout





 
	struct SwrContext *aud_convert_ctx = swr_alloc_set_opts(NULL, out_channel_layout, out_sample_fmt, out_sample_rate,
	out_channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
	printf("输入声道数:%d",pCodecCtx->channels);
	printf("输入采样率大小:%d",pCodecCtx->sample_rate);	
	swr_init(aud_convert_ctx);   //初始化

	AVFrame *pFrame = av_frame_alloc();  //this only allocates the avframe itself, not the data buffers
	AVPacket *pkt = (AVPacket *)av_malloc(sizeof(AVPacket));
	av_init_packet(pkt);
	int frame_cnt = 0;
	int get_frame;
	int time=0;
	while (av_read_frame(pFormatCtx, pkt) >= 0)
	{
		time++;
		if (pkt->stream_index == index_audio)
		{			
			if (avcodec_decode_audio4(pCodecCtx, pFrame, &get_frame, pkt) < 0)
			{
				printf("Decode Error!\n");
				return -1;
			}
			if (get_frame)
			{
				//printf("Decoded frame index: %d\n", frame_cnt);   //一个循环解码了多少帧
				swr_convert(aud_convert_ctx, &out_buffer,2*22050, (const uint8_t **)pFrame->data, pFrame->nb_samples);//1024
				//printf("pFrame->nb_samples=%d\n",pFrame->nb_samples);1024
				memcpy(buffer_pcm_s16,out_buffer,2048);
				fwrite(out_buffer,1, 2048, file_s16);
				//frame_cnt++;
			}
			break;
			printf("解码成功:%d\n",time);
		}
	}
	avformat_free_context(pFormatCtx);
	//av_frame_unref(pFrame);
	//av_free_packet(packet);
	//av_free(avio);
	//swr_free(aud_convert_ctx);
	//free(aviobuffer);
	//free(out_buffer);
	//free(pkt);
//avcodec_close();

	return 0;
}


void PlayInit()
{
	if ((err = snd_pcm_open (&capture_handle, "default",SND_PCM_STREAM_PLAYBACK,0))<0) 
	{
		printf("无法打开音频设备: %s (%s)\n","default",snd_strerror (err));
		exit(1);
	}
	if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 
	{
		printf("无法分配硬件参数结构 (%s)\n",snd_strerror(err));
		exit(1);
	}	
	if((err=snd_pcm_hw_params_any(capture_handle,hw_params)) < 0) 
	{
		printf("无法初始化硬件参数结构 (%s)\n", snd_strerror(err));
		exit(1);
	}
	if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 
	{
		printf("无法设置访问类型(%s)\n",snd_strerror(err));
		exit(1);
	}
	if ((err=snd_pcm_hw_params_set_format(capture_handle, hw_params,format)) < 0) 
	{
		printf("无法设置格式 (%s)\n",snd_strerror(err));
		exit(1);
	}
	if((err=snd_pcm_hw_params_set_rate_near(capture_handle,hw_params,&rate,0))<0) 
	{
		printf("无法设置采样率(%s)\n",snd_strerror(err));
		exit(1);
	}
	if ((err=snd_pcm_hw_params (capture_handle,hw_params))<0) 
	{
		printf("无法向驱动程序设置参数(%s)\n",snd_strerror(err));
		exit(1);
	}
	//snd_pcm_hw_params_get_channels(hw_params,&val);
	//printf("channels=%d\n",val);
	//snd_pcm_hw_params_get_rate(hw_params,&val, &dir);
	//printf("rate=%d\n",val);
//	snd_pcm_hw_params_get_format(hw_params,format_la);
//	printf("%s",format_la);
/****************************************************************************************/
	//snd_pcm_hw_params_set_buffer_size_near(capture_handle,hw_params,snd_pcm_uframes_tt);//设置声卡缓冲区大小
	snd_pcm_hw_params_free(hw_params);//释放
	frame_byte=snd_pcm_format_width(format)/8;
}
//snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U)指导什么时候开启声卡,300帧  writei先写道alsa中,到了300才开始播放
//snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);//设置音频数据的最接近目标的采样率
//snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
//https://blog.csdn.net/yuan1125/article/details/45502527
void Play()
{
	i++;
	printf("第%d次成功\n",i);
	if((err=snd_pcm_prepare(capture_handle))<0) 
	{
		printf("无法使用音频接口 (%s)\n",snd_strerror(err));
		exit(1);
	}
/*使用此函数的目的是避免出现由于网络原因,声卡不能及时得到音频数据而使得 snd_pcm_writei() 不能正常连续工作。
实际上在 xrun_recovery( ) 中,又调用 snd_pcm_prepare() 和 snd_pcm_resume() 以实现能“恢复错误”的功能。
-EPIPE 错误表示应用程序没有及时把 PCM 采样数据送入ASLA 库。
*/
	
	if((err=snd_pcm_writei(capture_handle,buffer_pcm_s16,buffer_frames))!=buffer_frames) 
	{
		printf("向音频接口写数据失败(%s)\n,%d\n",snd_strerror(err),err);
		exit(1);
	}
	//usleep(30000);
	//printf("向音频接口写数据失败(%s)\n",snd_strerror(err));
	//fwrite(buffer_pcm_s16,(buffer_frames*AUDIO_CHANNEL_SET),frame_byte,pcm_data_file2);
	//file_pcm_fltp
}


void main()
{
	//file_pcm_fltp = fopen("123fltp.pcm", "wb");
	file_la = fopen("123la.aac", "wb");
	//pcm_data_file2= fopen("666.pcm", "wb");///写进声卡的pcm
	//file_s16 = fopen("las16.pcm", "wb");//重采样的
    file_s16 = fopen("output.pcm", "wb");
	//DecodeInit();
	//SwreSampleInit();
	PlayInit();
	while(1)
	{
		printf("进入循环\n");
		
		ReceiveRtp(); f:1ms
		if(nRecEcho>0)
		{
			//usleep(30000);
			Decode_OK();
			nRecEcho=0;
			Play();
		}
		else
			continue;
		
		//
		//sleep(10);
		//buffer_pcm_s16[2048]='0';
		//usleep(70000);
	}
	//av_free_packet(pkt);

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_42475191

谢谢老板

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值