FFMPEG-数据结构解释(AVCodecContext,AVStream,AVFormatContext)_Howie的专栏-CSDN博客
FFmpeg学习4:音频格式转换_ansu1912的博客-CSDN博客
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);
}