//
// TyeFFmpegC.c
// TyeFFmpegToMacOS
//
// Created by Black2 on 2023/4/15.
//
#include "TyeFFmpegC.h"
void yeTestDebug(void){
printf("Tye-C \n");
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_DEBUG, "Tye 测试 FFmpeg API\n");
return;
}
///== 6-8打开音频设备(内置的、外接的):
/// #include "libavdevice/avdevice.h"
///#include "libavformat/avformat.h"
AVFormatContext* yeFFmpeg01(void){
// 1. 注册设备:
avdevice_register_all();
/// 2. 设置采集方式:avfoundation(MacOS) / dshow(Window)/ alsa(Linux):
AVInputFormat * eInputFormat = av_find_input_format("avfoundation");
/// 3.打开音频设备:
AVFormatContext * eAVFormatContext = NULL;
char * eDeviceName = ":0"; //第一个设备; [[video device]:[audio device]]
AVDictionary * eDic = NULL;
int eIntRet = avformat_open_input(&eAVFormatContext, eDeviceName, eInputFormat, &eDic);
char eArrCharError[1024];
if(eIntRet < 0){ // 出错;
av_strerror(eIntRet, eArrCharError, 1024);
fprintf(stderr,"---打开设备,错误信息: %d==%s--\n",eIntRet, eArrCharError);
return NULL;
}else{
printf("---打开设备 成功--\n");
return &eAVFormatContext;
}
}
///==6-9从音频设备中读取音频数据: av_read_frame(<#AVFormatContext *s#>, <#AVPacket *pkt#>)
//#include "libavcodec/avcodec.h"
void yeFFmpeg02(void){
//av_read_frame(<#AVFormatContext *s#>, <#AVPacket *pkt#>) 返回0:表示成功;
//AVFormatContext: 上下文;
//AVPacket: 音/视频包; data, size;
//av_init_packet(<#AVPacket *pkt#>) :1.1初始化引用对象;【成对用】
//av_packet_unref(<#AVPacket *pkt#>) :1.2释放(缓冲区的数据);
//av_packet_alloc() // 2.1开堆内存; 【成对用】
//av_packet_free(<#AVPacket **pkt#>) //2.2释放堆内存;
///-- 1. 打开音频设备:
AVFormatContext * eAVFormatContext = yeFFmpeg01();
if (NULL == eAVFormatContext){ return; }
///-- 2.读取音频数据:
int eIntCount = 0;//Test;
int eIntRet = -1;
AVPacket eAVPacket;
av_init_packet(&eAVPacket); //初始化;
while (0 == (eIntRet = av_read_frame(eAVFormatContext, &eAVPacket)) && eIntCount++ < 500) {
printf("读到的大小及数据:%d==%p", eAVPacket.size, eAVPacket.data); //4096;
av_packet_unref(&eAVPacket); //释放AVPacket(缓冲区的数据);
}
avformat_close_input(&eAVFormatContext); //release上下文, 关闭读取;
}
///== 6-11录制音频数据: 创建文件-->写入数据-->关闭文件;
void yeFFmpeg03(void){
// 1/3 w:写数据; r:读数据; wb:写二进制数据; wb+:创建文件写二进制数据;
char * eCharPath = "/Users/edy/Desktop/Test/TyeAudio.pcm";
FILE * eFile = fopen(eCharPath, "wb+");
///-- 1. 打开音频设备:
AVFormatContext * eAVFormatContext = yeFFmpeg01();
if (NULL == eAVFormatContext){ return; }
///-- 2.读取音频数据:
int eIntCount = 0;//Test;
int eIntRet = -1;
AVPacket eAVPacket;
av_init_packet(&eAVPacket); //初始化;
while (0 == (eIntRet = av_read_frame(eAVFormatContext, &eAVPacket)) && eIntCount++ < 500) {
printf("读到的大小及数据:%d==%p", eAVPacket.size, eAVPacket.data); //4096;
fwrite(eAVPacket.data, eAVPacket.size, 1, eFile); // 2/3 写入数据;
//fflush(eFile); //直接实时写入磁盘, 效率会降低;(不写入缓冲区)
av_packet_unref(&eAVPacket); //释放AVPacket(缓冲区的数据);
}
avformat_close_input(&eAVFormatContext); //release上下文, 关闭读取;
fclose(eFile); //3/3 关闭文件;
}
///== 8-4创建AAC编码器:
/// AVFrame: 保存未编码的数据;
/// AVPacket: 保存编码后的数据;
AVFrame * yeInitAVFrame(void){
AVFrame * eAVFrame = NULL;
eAVFrame = av_frame_alloc(); // 堆上分配内存;
if(!eAVFrame){
printf("---Error, No Memory!--\n");
goto __ERROR;
}
// 设置参数,set parameters
eAVFrame->nb_samples = 512; //单通道一个音频帧的采样数
eAVFrame->format = AV_SAMPLE_FMT_S16; //每个采样的大小
eAVFrame->channel_layout = AV_CH_LAYOUT_STEREO; //channel layout
//--- alloc inner memory, 根据上面的三个参数分配buffer的大小:
av_frame_get_buffer(eAVFrame, 0); // 512 * 2 * = 2048;(即未编码前一帧的大小为2048)
if(!eAVFrame->data[0]){
printf("---Error, Failed to alloc buf in frame!--\n");
//内存泄漏
goto __ERROR;
}
return eAVFrame;
///--
__ERROR:
if(eAVFrame){
av_frame_free(&eAVFrame);
}
return NULL;
}
AVPacket * yeInitAVPacket(void){
AVPacket * eAVPacket = NULL;
eAVPacket = av_packet_alloc(); // 堆上分配内存;
if(!eAVPacket){
//...
}
return eAVPacket;
}
/ 打开编码器:
AVCodecContext* yeInitAVCodecContext(void){
// 1. 创建编码器: (两种方式)
const AVCodec * eAVCodec = NULL;
//eAVCodec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
//eAVCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
eAVCodec = avcodec_find_encoder_by_name("libfdk_aac");
// 2.创建 codec 上下文:
AVCodecContext * eAVCodecContext = NULL;
eAVCodecContext = avcodec_alloc_context3(eAVCodec);
// 3. 设置参数:
eAVCodecContext->sample_fmt = AV_SAMPLE_FMT_S16; //输入音频的采样大小
eAVCodecContext->channel_layout = AV_CH_LAYOUT_STEREO; //输入音频的channel layout
eAVCodecContext->channels = 2; //输入音频 channel 个数
eAVCodecContext->sample_rate = 44100; //输入音频的采样率
//eAVCodecContext->bit_rate = 64000; //码率的大小, AAC_LC: 128K, AAC HE: 64K, AAC HE V2: 32K;
eAVCodecContext->bit_rate = 0;// 根据profile文件自动设置;
eAVCodecContext->profile = FF_PROFILE_AAC_HE_V2; //bit_rate=0时才有效;
// 4. 打开编码器:
int eIntRet = -1;
eIntRet = avcodec_open2(eAVCodecContext, eAVCodec, NULL);
if(eIntRet < 0){
printf("yeInitAVCodecContext 出错");
}
return eAVCodecContext;
}
///#include "libswresample/swresample.h"
SwrContext* yeInitSwrContext(void){ //初始化采样上下文;
SwrContext *eSwrContext = NULL; //重采样的上下文;
eSwrContext = swr_alloc_set_opts(NULL, //上下文;
AV_CH_LAYOUT_STEREO, //输出channel布局
AV_SAMPLE_FMT_S16, //输出的采样格式
44100, //采样率
AV_CH_LAYOUT_STEREO, //输入channel布局
AV_SAMPLE_FMT_FLT, //输入的采样格式
44100, //输入的采样率
0, NULL);
if(!eSwrContext){
//...
}
// 初始化:
int eIntRet0 = swr_init(eSwrContext);
if(eIntRet0 < 0){
printf("---初始化采样上下文 出错--");
}
return eSwrContext;
}
void yeFFmpeg04(void){
/// w:写数据; r:读数据; wb:写二进制数据; wb+:创建文件写二进制数据;
char * eCharPath = "/Users/edy/Desktop/Test/TyeAudio.pcm";
FILE * eFile = fopen(eCharPath, "wb+");
// 打开编码器:
AVCodecContext * eAVCodecContext = yeInitAVCodecContext();
AVFrame * eAVFrame = yeInitAVFrame();
//AVPacket * eAVPacket = av_packet_alloc();
//
//采样上下文:
SwrContext * eSwrContext = yeInitSwrContext();
///-- 1. 打开音频设备:
AVFormatContext * eAVFormatContext = yeFFmpeg01();
if (NULL == eAVFormatContext){ return; }
///-- 2.读取音频数据:
int eIntCount = 0;//Test;
int eIntRet = -1;
AVPacket eAVPacket;
av_init_packet(&eAVPacket); //初始化;
//
uint8_t **src_data = NULL;
int src_linesize = 0;
//4096/4=1024/2=512
//创建输入缓冲区
av_samples_alloc_array_and_samples(&src_data, //输出缓冲区地址
&src_linesize, //缓冲区的大小
2, //通道个数
512, //单通道采样个数
AV_SAMPLE_FMT_FLT, //采样格式
0);
//创建输出缓冲区
uint8_t **dst_data = NULL;
int dst_linesize = 0;
av_samples_alloc_array_and_samples(&dst_data, //输出缓冲区地址
&dst_linesize, //缓冲区的大小
2, //通道个数
512, //单通道采样个数
AV_SAMPLE_FMT_S16, //采样格式
0);
while (0 == (eIntRet = av_read_frame(eAVFormatContext, &eAVPacket)) && eIntCount++ < 500) {
printf("读到的大小及数据:%d==%p", eAVPacket.size, eAVPacket.data); //4096;
//将重采样的数据拷贝到src_data[]中:
memcpy((void*)src_data[0], (void*)eAVPacket.data, eAVPacket.size);
/// 重采样:
swr_convert(eSwrContext, //重采样的上下文
dst_data, //输出结果缓冲区
512, //每个通道的采样数
(const uint8_t **)src_data, //输入缓冲区
512);
fwrite(dst_data[0], 1, dst_linesize, eFile);
av_packet_unref(&eAVPacket); //释放AVPacket(缓冲区的数据);
}
//
fclose(eFile);
//
if(src_data){
av_freep(&src_data[0]);
}
av_freep(src_data);
if(dst_data){
av_freep(&dst_data[0]);
}
av_freep(dst_data);
//
swr_free(&eSwrContext);
//
avformat_close_input(&eAVFormatContext); //release上下文, 关闭读取;
}
///== 8-6 AAC编码,输入输出数据:
///
///
void yeFFmpegToEncode(AVCodecContext * eAVCodecContext, AVFrame * eAVFrame, AVPacket * eAVPacket, FILE * eFile){
/// 把一帧数据输入给编码器:
int eIntFrameR = -1;
eIntFrameR = avcodec_send_frame(eAVCodecContext, eAVFrame);
/// 进行编码:
while (eIntFrameR > 0) { // 因为有缓存,有可能没有返回数据,也可能返回多个数据;所以用循环获取;
eIntFrameR = avcodec_receive_packet(eAVCodecContext, eAVPacket);
//if(eIntFrameR >= 0) // 获取成功; 继续循环获取剩余的数据;
if(eIntFrameR < 0){ //没有编码好的数据了;
if(eIntFrameR == AVERROR(EAGAIN) || eIntFrameR == AVERROR_EOF){
//没有编码好的数据了;
break;
}else{
printf("---编码器出错了, 退出程序--");
}
}
//--- 写入文件:
fwrite(eAVPacket->data, 1, eAVPacket->size, eFile);
fflush(eFile);
}
}
/// ///-- 重要API:
// 将一帧一帧的数据输入给编码器: 输入的是 Frame : AVFrame(未编码的数据);
//avcodec_send_frame(<#AVCodecContext *avctx#>, <#const AVFrame *frame#>)
// 收到数据后进行编码: 编码得到 Packet : AVPacket(编码后的数据);
//avcodec_receive_packet(<#AVCodecContext *avctx#>, <#AVPacket *avpkt#>)
void yeFFmpeg05(void){
///-- 1. w:写数据; r:读数据; wb:写二进制数据; wb+:创建文件写二进制数据;
char * eCharPath = "/Users/edy/Desktop/Test/TyeAudio.pcm";
FILE * eFile = fopen(eCharPath, "wb+");
///-- 2. 打开编码器:
AVCodecContext * eAVCodecContext = yeInitAVCodecContext();
//--
AVFrame * eAVFrame = yeInitAVFrame();
AVPacket * eAVPacket = yeInitAVPacket();
// av_init_packet(&eAVPacket); //初始化;
///-- 3.采样上下文:
SwrContext * eSwrContext = yeInitSwrContext();
///-- 4. 打开音频设备:
AVFormatContext * eAVFormatContext = yeFFmpeg01();
if (NULL == eAVFormatContext){ return; }
///-- 5.读取音频数据:
int eIntCount = 0;//Test;
int eIntRet = -1;
///-- 6.
uint8_t **src_data = NULL;
int src_linesize = 0;
//4096/4=1024/2=512
//创建输入缓冲区
av_samples_alloc_array_and_samples(&src_data, //输出缓冲区地址
&src_linesize, //缓冲区的大小
2, //通道个数
512, //单通道采样个数
AV_SAMPLE_FMT_FLT, //采样格式
0);
//创建输出缓冲区
uint8_t **dst_data = NULL;
int dst_linesize = 0;
av_samples_alloc_array_and_samples(&dst_data, //输出缓冲区地址
&dst_linesize, //缓冲区的大小
2, //通道个数
512, //单通道采样个数
AV_SAMPLE_FMT_S16, //采样格式
0);
///-- 7.
while (0 == (eIntRet = av_read_frame(eAVFormatContext, &eAVPacket)) && eIntCount++ < 500) {
printf("读到的大小及数据:%d==%p", eAVPacket->size, eAVPacket->data); //4096;
//将重采样的数据拷贝到src_data[]中:
memcpy((void*)src_data[0], (void*)eAVPacket->data, eAVPacket->size);
/// 重采样:
swr_convert(eSwrContext, //重采样的上下文
dst_data, //输出结果缓冲区
512, //每个通道的采样数
(const uint8_t **)src_data, //输入缓冲区
512);
/// 将重采样的数据复制到 AVFrame中:
memcpy((void*)eAVFrame->data[0], (void*)dst_data[0], dst_linesize);
yeFFmpegToEncode(eAVCodecContext, eAVFrame, eAVPacket, eFile); //编码;
av_packet_unref(&eAVPacket);
}
///-- 输入eAVFrame == NULL:告诉编码器没有数据再输入了;
yeFFmpegToEncode(eAVCodecContext, NULL, eAVPacket, eFile); //编码;
///-- 8.
fclose(eFile);
if(src_data){
av_freep(&src_data[0]);
}
av_freep(src_data);
if(dst_data){
av_freep(&dst_data[0]);
}
av_freep(dst_data);
swr_free(&eSwrContext);
avformat_close_input(&eAVFormatContext); //release上下文, 关闭读取;
}
void yeFFmpeg00(void){
}
void yeTestFFmpeg(int eInt){
yeFFmpeg02();
return;
}
FFmpeg代码备份
最新推荐文章于 2024-09-29 13:36:59 发布
该代码示例展示了如何使用FFmpeg库在MacOS上打开音频设备,读取音频数据,以及进行AAC编码。它涉及到avformat_open_input来打开设备,av_read_frame来读取音频帧,以及avcodec_send_frame和avcodec_receive_packet进行音频编码。
摘要由CSDN通过智能技术生成