Program 1:在windows上测试。 存在问题,如果频率重采样前后的频率不一样,会导致音频数据丢失。
#define __STDC_CONSTANT_MACROS
#include <string.h>
#include <windows.h>
#include <DShow.h>
extern "C"
{
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
}
static char *dup_wchar_to_utf8(wchar_t *w)
{
char *s = NULL;
int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
s = (char *)av_malloc(l);
if (s)
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
return s;
}
/**
* @brief open audio device
* @return succ: AVFormatContext*, fail: NULL
*/
static
AVFormatContext* open_dev(){
int ret = 0;
char errors[1024] = { 0, };
//ctx
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
av_dict_set(&options, "sample_size", "16", 0);
av_dict_set(&options, "channels", "1", 0);
av_dict_set(&options, "sample_rate", "16000", 0);
char * device_name = dup_wchar_to_utf8(L"audio=麦克风 (Microphone)");
//get format
AVInputFormat *iformat = av_find_input_format("dshow");
//open device
if ((ret = avformat_open_input(&fmt_ctx, device_name, iformat, &options)) < 0){
av_strerror(ret, errors, 1024);
fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);
return NULL;
}
return fmt_ctx;
}
void rec_audio() {
//context
AVFormatContext *fmt_ctx = NULL;
//set log level
av_log_set_level(AV_LOG_DEBUG);
//register audio device
avdevice_register_all();
//create file1
char *out1 = "./16k_mono_s16le.pcm";
FILE *outfile1 = fopen(out1, "wb+");
if (!outfile1){
printf("Error, Failed to open file!\n");
return;
}
//create file2
char *out2 = "./16k_stereo_s16le.pcm";
FILE *outfile2 = fopen(out2, "wb+");
if (!outfile2){
printf("Error, Failed to open file!\n");
return;
}
//打开设备
fmt_ctx = open_dev();
if (!fmt_ctx){
printf("Error, Failed to open device!\n");
return;
}
AVPacket pkt;
int ret = -1, count = 1;
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_MONO, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swr, "in_sample_rate", 16000, 0);
av_opt_set_int(swr, "out_sample_rate", 16000, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);//-f f32le
/* initialize the resampling context */
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
}
uint8_t **out_data = NULL;
uint8_t **in_data = NULL;
int dst_linesize, src_linesize;
int nbsamples = 44100 / 2;
av_samples_alloc_array_and_samples(&in_data, &src_linesize, 1, nbsamples, AV_SAMPLE_FMT_S16, 0);
av_samples_alloc_array_and_samples(&out_data, &dst_linesize, 2, nbsamples, AV_SAMPLE_FMT_FLT, 0);
while ((ret = av_read_frame(fmt_ctx, &pkt)) == 0 && count++ < 10)
{
av_log(NULL, AV_LOG_INFO, "packet size is %d\n", pkt.size);
memcpy((void *)in_data[0], pkt.data, pkt.size);
int ret = swr_convert(swr, out_data, nbsamples, (const uint8_t **)in_data, nbsamples);
int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, 2, ret, AV_SAMPLE_FMT_FLT, 1);
fwrite(pkt.data, 1, pkt.size, outfile1);
fwrite(out_data[0], 1, dst_bufsize, outfile2);
fflush(outfile1);
fflush(outfile2);
av_packet_unref(&pkt);//release pkt
}
fclose(outfile1);
fclose(outfile2);
av_log(NULL, AV_LOG_DEBUG, "finish!\n");
return;
}
int main(int argc, char *argv[])
{
rec_audio();
return 0;
}
program2: 解决program1 的问题,上采样和下采样av_rescale_rnd来就算dst_nb_samples
#define __STDC_CONSTANT_MACROS
#include <string.h>
#include <windows.h>
#include <DShow.h>
extern "C"
{
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
}
static char *dup_wchar_to_utf8(wchar_t *w)
{
char *s = NULL;
int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
s = (char *)av_malloc(l);
if (s)
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
return s;
}
/**
* @brief open audio device
* @return succ: AVFormatContext*, fail: NULL
*/
static
AVFormatContext* open_dev(){
int ret = 0;
char errors[1024] = { 0, };
//ctx
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
av_dict_set(&options, "sample_size", "16", 0);
av_dict_set(&options, "channels", "1", 0);
av_dict_set(&options, "sample_rate", "16000", 0);
char * device_name = dup_wchar_to_utf8(L"audio=麦克风 (Microphone)");
//get format
AVInputFormat *iformat = av_find_input_format("dshow");
//open device
if ((ret = avformat_open_input(&fmt_ctx, device_name, iformat, &options)) < 0){
av_strerror(ret, errors, 1024);
fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);
return NULL;
}
return fmt_ctx;
}
void rec_audio() {
//context
AVFormatContext *fmt_ctx = NULL;
//set log level
av_log_set_level(AV_LOG_DEBUG);
//register audio device
avdevice_register_all();
//create file1
char *out1 = "./16k_mono_s16le.pcm";
FILE *outfile1 = fopen(out1, "wb+");
if (!outfile1){
printf("Error, Failed to open file!\n");
return;
}
//create file2
char *out2 = "./41k_stereo_f32le.pcm";
FILE *outfile2 = fopen(out2, "wb+");
if (!outfile2){
printf("Error, Failed to open file!\n");
return;
}
//打开设备
fmt_ctx = open_dev();
if (!fmt_ctx){
printf("Error, Failed to open device!\n");
return;
}
AVPacket pkt;
int ret = -1, count = 1;
uint64_t src_channel_layout = AV_CH_LAYOUT_MONO;
uint64_t dst_channel_layout = AV_CH_LAYOUT_STEREO; //定义目标音频参数
int src_rate = 16000;
int dst_rate = 44100;
AVSampleFormat src_fmt = AV_SAMPLE_FMT_S16;
AVSampleFormat dst_fmt = AV_SAMPLE_FMT_FLT;
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", src_channel_layout, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", dst_channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", src_rate, 0);
av_opt_set_int(swr, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", src_fmt, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", dst_fmt, 0);//-f f32le
/* initialize the resampling context */
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
}
uint8_t **out_data = NULL;
uint8_t **in_data = NULL;
int dst_linesize, src_linesize;
int src_nb_samples = 44100 / av_get_bytes_per_sample(src_fmt);
int max_dst_nb_samples, dst_nb_samples;
int src_channels = av_get_channel_layout_nb_channels(src_channel_layout);
int dst_channels = av_get_channel_layout_nb_channels(dst_channel_layout);
av_log(NULL, AV_LOG_INFO, "src_channels = %d, dst_channels = %d\n", src_channels, dst_channels);
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
av_samples_alloc_array_and_samples(&in_data, &src_linesize, src_channels, src_nb_samples, src_fmt, 0);
av_samples_alloc_array_and_samples(&out_data, &dst_linesize, dst_channels, dst_nb_samples, dst_fmt, 0);
while ((ret = av_read_frame(fmt_ctx, &pkt)) == 0 && count++ < 10)
{
av_log(NULL, AV_LOG_INFO, "packet size is %d\n", pkt.size);
memcpy((void *)in_data[0], pkt.data, pkt.size);
/* compute destination number of samples */
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr, src_rate) + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
int ret = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_channels, ret, dst_fmt, 1);
fwrite(pkt.data, 1, pkt.size, outfile1);
fwrite(out_data[0], 1, dst_bufsize, outfile2);//ffplay -ar 44100 -ac 2 -f f32le 41k_stereo_f32le.pcm
fflush(outfile1);
fflush(outfile2);
av_packet_unref(&pkt);//release pkt
}
fclose(outfile1);
fclose(outfile2);
av_log(NULL, AV_LOG_DEBUG, "finish!\n");
return;
}
int main(int argc, char *argv[])
{
rec_audio();
return 0;
}
Program 3: Ubuntu上测试。 重新计算dst_sample。
#define __STDC_CONSTANT_MACROS
#include <string.h>
extern "C"
{
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
}
/**
* @brief open audio device
* @return succ: AVFormatContext*, fail: NULL
*/
static
AVFormatContext* open_dev(){
int ret = 0;
char errors[1024] = { 0, };
//ctx
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
av_dict_set(&options, "sample_size", "16", 0);
av_dict_set(&options, "channels", "1", 0);
av_dict_set(&options, "sample_rate", "16000", 0);
//[[video device]:[audio device]]
char *device_name = "hw:2,0";
//get format
AVInputFormat *iformat = av_find_input_format("alsa");
//open device
if ((ret = avformat_open_input(&fmt_ctx, device_name, iformat, &options)) < 0){
av_strerror(ret, errors, 1024);
fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);
return NULL;
}
return fmt_ctx;
}
void rec_audio() {
//context
AVFormatContext *fmt_ctx = NULL;
//set log level
av_log_set_level(AV_LOG_DEBUG);
//register audio device
avdevice_register_all();
//create file1
char *out1 = "/home/lili/Videos/16k_mono_s16le.pcm";
FILE *outfile1 = fopen(out1, "wb+");
if (!outfile1){
printf("Error, Failed to open file!\n");
return;
}
//create file2
char *out2 = "/home/lili/Videos/16k_stereo_s16le.pcm";
FILE *outfile2 = fopen(out2, "wb+");
if (!outfile2){
printf("Error, Failed to open file!\n");
return;
}
//打开设备
fmt_ctx = open_dev();
if (!fmt_ctx){
printf("Error, Failed to open device!\n");
return;
}
AVPacket pkt;
int ret = -1, count = 1;
uint64_t src_channel_layout = AV_CH_LAYOUT_MONO;
uint64_t dst_channel_layout = AV_CH_LAYOUT_STEREO; //定义目标音频参数
int src_rate = 16000;
int dst_rate = 44100;
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", src_channel_layout, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", dst_channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", src_rate, 0);
av_opt_set_int(swr, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
/* initialize the resampling context */
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
return;
}
uint8_t **out_data = NULL;
uint8_t **in_data = NULL;
int dst_linesize, src_linesize;
int src_nb_samples = 128;
int max_dst_nb_samples, dst_nb_samples;
int src_channels = av_get_channel_layout_nb_channels(src_channel_layout);
int dst_channels = av_get_channel_layout_nb_channels(dst_channel_layout);
av_log(NULL, AV_LOG_INFO, "src_channels = %d, dst_channels = %d\n", src_channels, dst_channels);
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
av_samples_alloc_array_and_samples(&in_data, &src_linesize, src_channels, src_nb_samples, AV_SAMPLE_FMT_S16, 0);
av_samples_alloc_array_and_samples(&out_data, &dst_linesize, dst_channels, dst_nb_samples, AV_SAMPLE_FMT_FLT, 0);
av_log(NULL, AV_LOG_INFO, "src_linesize = %d, dst_linesize = %d\n", src_linesize, dst_linesize);
while((ret = av_read_frame(fmt_ctx, &pkt)) == 0 && count++ < 1024)
{
av_log(NULL, AV_LOG_INFO, "packet size is %d\n", pkt.size);
memcpy((void *)in_data[0], pkt.data, pkt.size);
/* compute destination number of samples */
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr, src_rate) + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
int ret = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, 2, ret, AV_SAMPLE_FMT_FLT, 1);
fwrite(pkt.data, 1, pkt.size, outfile1);
fwrite(out_data[0], 1, dst_bufsize, outfile2);
fflush(outfile1);
fflush(outfile2);
av_packet_unref(&pkt);//release pkt
}
fclose(outfile1);
fclose(outfile2);
if (out_data)
av_freep(&out_data[0]);
av_freep(&out_data);
if (in_data)
av_freep(&in_data[0]);
av_freep(&in_data);
swr_free(&swr);//释放重采样上下文资源
av_log(NULL, AV_LOG_DEBUG, "finish!\n");
return;
}
int main(int argc, char *argv[])
{
rec_audio();
return 0;
}
Program4:
PCM数据的重采样,然后编码
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h"
}
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#define ADTS_HEADER_LEN 7;
void adts_header(char *szAdtsHeader, int dataLen){
int audio_object_type = 2;
int sampling_frequency_index = 4;//4: 44100 Hz
int channel_config = 2;
int adtsLen = dataLen + 7;
szAdtsHeader[0] = 0xff; //syncword:0xfff 高8bits
szAdtsHeader[1] = 0xf0; //syncword:0xfff 低4bits
szAdtsHeader[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
szAdtsHeader[1] |= (0 << 1); //Layer:0 2bits
szAdtsHeader[1] |= 1; //protection absent:1 1bit
szAdtsHeader[2] = (audio_object_type - 1) << 6; //profile:audio_object_type - 1 2bits
szAdtsHeader[2] |= (sampling_frequency_index & 0x0f) << 2; //sampling frequency index:sampling_frequency_index 4bits
szAdtsHeader[2] |= (0 << 1); //private bit:0 1bit
szAdtsHeader[2] |= (channel_config & 0x04) >> 2; //channel configuration:channel_config 高1bit
szAdtsHeader[3] = (channel_config & 0x03) << 6; //channel configuration:channel_config 低2bits
szAdtsHeader[3] |= (0 << 5); //original:0 1bit
szAdtsHeader[3] |= (0 << 4); //home:0 1bit
szAdtsHeader[3] |= (0 << 3); //copyright id bit:0 1bit
szAdtsHeader[3] |= (0 << 2); //copyright id start:0 1bit
szAdtsHeader[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
szAdtsHeader[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bits
szAdtsHeader[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value 低3bits
szAdtsHeader[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
szAdtsHeader[6] = 0xfc;
}
static void encode(AVCodecContext *cdc_ctx, AVFrame *frame, AVPacket *pkt, FILE *fp_out)
{
int ret = 0;
if ((ret = avcodec_send_frame(cdc_ctx, frame)) < 0)
{
fprintf(stderr, "avcodec_send_frame failed.\n");
exit(1);
}
while ((ret = avcodec_receive_packet(cdc_ctx, pkt)) >= 0)
{
char adts_header_buf[7];
adts_header(adts_header_buf, pkt->size);
fwrite(adts_header_buf, 1, 7, fp_out);
printf("Write (size=%d) packet.\n", pkt->size);
fwrite(pkt->data, 1, pkt->size, fp_out);
av_packet_unref(pkt);
}
if ((ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF))
{
fprintf(stderr, "avcodec_receive_packet failed.\n");
exit(1);
}
}
const char *fifo_name = "/home/lili/Audio/myfifo";
int create_pipe()
{
if(mkfifo("./fifo",0777) != 0 ) //在当前路径下(运行程序所在的路径)创建有名管道,有名管道权限读写执行
{
if(errno == EEXIST) //当该有名管道存在时,提示下
{
printf("File exists\n");
}
else
{
perror("mkfifo fail ");
exit(1);
}
}
int pipe_fd;
pipe_fd = open(fifo_name,O_RDWR);//读写方式打开,使用文件IO 操作有名管道
if(pipe_fd < 0)
{
perror("open fifo fail: ");
exit(1);
}
return pipe_fd;
}
AVFrame *frame = NULL;
AVCodecContext *codec_ctx = NULL;
AVPacket *pkt = NULL;
FILE *fp_aacout = NULL;
int pipefd[2];
void *thr_fn(void *arg){
printf("new thread \n");
int pipe_fd = open(fifo_name, O_RDONLY);
int readn = 0;
if(frame == NULL || codec_ctx == NULL || pkt == NULL)
{
printf("pointer is NULL!\n");
return NULL;
}
while((readn = read(pipe_fd, frame->data[0], frame->linesize[0])) > 0)
{
printf("read %d data\n", readn);
encode(codec_ctx, frame, pkt, fp_aacout);
usleep(100);
}
encode(codec_ctx, NULL, pkt, fp_aacout);
close(pipe_fd);
printf("exit thread!\n");
return ((void *)0);
}
int main(int argc, char *argv[])
{
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
int64_t dst_ch_layout = AV_CH_LAYOUT_MONO;
int src_rate = 16000;
int dst_rate = 44100;
AVSampleFormat src_fmt = AV_SAMPLE_FMT_S16;
AVSampleFormat dst_fmt = AV_SAMPLE_FMT_FLTP;
const char *input_file = "/home/lili/Audio/QLED_16000_mono_s16le.pcm";
const char *output_file = "/home/lili/Audio/QLED_44100_mono_f32le.pcm";
const char *output_aacfile = "/home/lili/Audio/QLED.aac";
avdevice_register_all();
//查找编码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!codec)
{
printf("can't find encoder\n");
return -1;
}
if ((codec_ctx = avcodec_alloc_context3(codec)) == NULL)
{
fprintf(stderr, "avcodec_alloc_context3 failed.\n");
}
codec_ctx->bit_rate = 64000;
codec_ctx->sample_fmt = dst_fmt;
codec_ctx->sample_rate = dst_rate;
codec_ctx->channel_layout = dst_ch_layout;
codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout);
//打开编码器
if (avcodec_open2(codec_ctx, codec, NULL) < 0)
{
printf("can't open encoder\n");
}
if ((pkt = av_packet_alloc()) == NULL)
{
fprintf(stderr, "av_packet_alloc failed.\n");
}
frame = av_frame_alloc();
if (!frame)
{
printf("can't alloc frame\n");
return -1;
}
frame->nb_samples = codec_ctx->frame_size;
frame->format = dst_fmt;
frame->channel_layout = dst_ch_layout;
int ret = -1;
if ((ret = av_frame_get_buffer(frame, 0)) < 0)
{
fprintf(stderr, "av_frame_get_buffer failed.\n");
}
FILE *fp_in = NULL;
if ((fp_in = fopen(input_file, "rb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", input_file);
return -1;
}
FILE *fp_out = NULL;
if ((fp_out = fopen(output_file, "wb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", output_file);
return -1;
}
int data_size = av_get_bytes_per_sample(src_fmt);
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", src_ch_layout, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", dst_ch_layout, 0);
av_opt_set_int(swr, "in_sample_rate", src_rate, 0);
av_opt_set_int(swr, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", src_fmt, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", dst_fmt, 0);
/* initialize the resampling context */
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
}
uint8_t **out_data = NULL;
uint8_t **in_data = NULL;
int dst_linesize, src_linesize;
int src_nb_samples = frame->nb_samples;
int max_dst_nb_samples, dst_nb_samples;
int src_channels = av_get_channel_layout_nb_channels(src_ch_layout);
int dst_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
av_log(NULL, AV_LOG_INFO, "src_channels = %d, dst_channels = %d\n", src_channels, dst_channels);
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
av_samples_alloc_array_and_samples(&in_data, &src_linesize, src_channels, src_nb_samples, src_fmt, 0);
av_samples_alloc_array_and_samples(&out_data, &dst_linesize, dst_channels, dst_nb_samples, dst_fmt, 0);
if ((fp_aacout = fopen(output_aacfile, "wb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", output_aacfile);
return -1;
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
int readn = 0;
while((readn = read(pipefd[0], frame->data[0], frame->linesize[0])) > 0)
{
printf("read %d data\n", readn);
encode(codec_ctx, frame, pkt, fp_aacout);
usleep(100);
}
encode(codec_ctx, NULL, pkt, fp_aacout);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else
{ /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
while (feof(fp_in) == 0)
{
//输入一帧数据的长度
int length = src_nb_samples * av_get_bytes_per_sample(src_fmt) * src_channels;
fread(frame->data[0], 1, length, fp_in);
memcpy((void *)in_data[0], frame->data[0], length);
/* compute destination number of samples */
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr, src_rate) + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
int ret = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
int dst_bufsize = av_samples_get_buffer_size(NULL, dst_channels, ret, dst_fmt, 1);
fprintf(stderr, "writing %d data\n",dst_bufsize);
fwrite(out_data[0], 1, dst_bufsize, fp_out);//ffplay -ar 44100 -ac 1 -f f32le 41k_stereo_f32le.pcm
write(pipefd[1], out_data[0], dst_bufsize); //子进程写数据给管道
fflush(fp_out);
}
close(pipefd[1]); /* Reader will see EOF */
fclose(fp_out);
fclose(fp_aacout);
fclose(fp_in);
av_frame_free(&frame);
av_packet_free(&pkt);
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
return 0;
}
上述程序存在两个问题,如果swr_convert转换成双通道,Program4没有考虑到。如果是自己实现,需要将转换后的数据按照编码器要求的格式,nb_samples,存放到frame中。
int convert_samples = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
convert_samples大于src_nb_samples,需要对数据重新分配,还要考虑PCM的模式是packet,planner模式,并存储到frame的data中,以及是几个声道的一堆事情。
好在有av_audio_fifo_write,av_audio_fifo_read来帮我们做这件事情。
Program5: 用av_audio_fifo替换pipe。不用从底层的字节层面去做转换,效率更高。
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavdevice/avdevice.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"
}
#include <stdio.h>
#define ADTS_HEADER_LEN 7;
static void adts_header(char *szAdtsHeader, int dataLen){
int audio_object_type = 2;
int sampling_frequency_index = 4;//4: 44100 Hz
int channel_config = 2;
int adtsLen = dataLen + 7;
szAdtsHeader[0] = 0xff; //syncword:0xfff 高8bits
szAdtsHeader[1] = 0xf0; //syncword:0xfff 低4bits
szAdtsHeader[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
szAdtsHeader[1] |= (0 << 1); //Layer:0 2bits
szAdtsHeader[1] |= 1; //protection absent:1 1bit
szAdtsHeader[2] = (audio_object_type - 1) << 6; //profile:audio_object_type - 1 2bits
szAdtsHeader[2] |= (sampling_frequency_index & 0x0f) << 2; //sampling frequency index:sampling_frequency_index 4bits
szAdtsHeader[2] |= (0 << 1); //private bit:0 1bit
szAdtsHeader[2] |= (channel_config & 0x04) >> 2; //channel configuration:channel_config 高1bit
szAdtsHeader[3] = (channel_config & 0x03) << 6; //channel configuration:channel_config 低2bits
szAdtsHeader[3] |= (0 << 5); //original:0 1bit
szAdtsHeader[3] |= (0 << 4); //home:0 1bit
szAdtsHeader[3] |= (0 << 3); //copyright id bit:0 1bit
szAdtsHeader[3] |= (0 << 2); //copyright id start:0 1bit
szAdtsHeader[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
szAdtsHeader[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bits
szAdtsHeader[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value 低3bits
szAdtsHeader[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
szAdtsHeader[6] = 0xfc;
}
static void encode(AVCodecContext *cdc_ctx, AVFrame *frame, AVPacket *pkt, FILE *fp_out)
{
int ret = 0;
if ((ret = avcodec_send_frame(cdc_ctx, frame)) < 0)
{
fprintf(stderr, "avcodec_send_frame failed.\n");
exit(1);
}
while ((ret = avcodec_receive_packet(cdc_ctx, pkt)) >= 0)
{
char adts_header_buf[7];
adts_header(adts_header_buf, pkt->size);
fwrite(adts_header_buf, 1, 7, fp_out);
printf("Write (size=%d) packet.\n", pkt->size);
fwrite(pkt->data, 1, pkt->size, fp_out);
av_packet_unref(pkt);
}
if ((ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF))
{
fprintf(stderr, "avcodec_receive_packet failed.\n");
exit(1);
}
}
static AVFrame *frame = NULL;
static AVCodecContext *codec_ctx = NULL;
static AVPacket *pkt = NULL;
static FILE *fp_aacout = NULL;
int main(int argc, char *argv[])
{
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
int src_rate = 8000;
int dst_rate = 44100;
AVSampleFormat src_fmt = AV_SAMPLE_FMT_S16;
AVSampleFormat dst_fmt = AV_SAMPLE_FMT_FLTP;
const char *input_file = "./beijingbeijing_8000_mono_s16le.pcm";
const char *output_file = "./beijingbeijing_44100_stereo_f32le.pcm";
const char *output_aacfile = "./beijingbeijing.aac";
avdevice_register_all();
//查找编码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!codec)
{
printf("can't find encoder\n");
return -1;
}
if ((codec_ctx = avcodec_alloc_context3(codec)) == NULL)
{
fprintf(stderr, "avcodec_alloc_context3 failed.\n");
return -1;
}
codec_ctx->bit_rate = 64000;
codec_ctx->sample_fmt = dst_fmt;
codec_ctx->sample_rate = dst_rate;
codec_ctx->channel_layout = dst_ch_layout;
codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout);
//打开编码器
if (avcodec_open2(codec_ctx, codec, NULL) < 0)
{
printf("can't open encoder\n");
return -1;
}
if ((pkt = av_packet_alloc()) == NULL)
{
fprintf(stderr, "av_packet_alloc failed.\n");
return -1;
}
frame = av_frame_alloc();
if (!frame)
{
printf("can't alloc frame\n");
return -1;
}
frame->nb_samples = codec_ctx->frame_size;
frame->format = dst_fmt;
frame->channel_layout = dst_ch_layout;
int ret = -1;
if ((ret = av_frame_get_buffer(frame, 0)) < 0)
{
fprintf(stderr, "av_frame_get_buffer failed.\n");
return -1;
}
FILE *fp_in = NULL;
if ((fp_in = fopen(input_file, "rb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", input_file);
return -1;
}
FILE *fp_out = NULL;
if ((fp_out = fopen(output_file, "wb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", output_file);
return -1;
}
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", src_ch_layout, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", dst_ch_layout, 0);
av_opt_set_int(swr, "in_sample_rate", src_rate, 0);
av_opt_set_int(swr, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", src_fmt, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", dst_fmt, 0);
/* initialize the resampling context */
if ((ret = swr_init(swr)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
return -1;
}
uint8_t **out_data = NULL;
uint8_t **in_data = NULL;
int dst_linesize, src_linesize;
int src_nb_samples = frame->nb_samples;
int max_dst_nb_samples, dst_nb_samples;
int src_channels = av_get_channel_layout_nb_channels(src_ch_layout);
int dst_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
av_log(NULL, AV_LOG_INFO, "src_channels = %d, dst_channels = %d\n", src_channels, dst_channels);
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
av_samples_alloc_array_and_samples(&in_data, &src_linesize, src_channels, src_nb_samples, src_fmt, 0);
av_samples_alloc_array_and_samples(&out_data, &dst_linesize, dst_channels, dst_nb_samples, dst_fmt, 0);
if ((fp_aacout = fopen(output_aacfile, "wb")) == NULL)
{
fprintf(stderr, "fopen %s failed.\n", output_aacfile);
return -1;
}
AVAudioFifo *fifo = av_audio_fifo_alloc(dst_fmt, dst_channels, frame->nb_samples);
if ((ret = av_audio_fifo_realloc(fifo, frame->nb_samples)) < 0) {
fprintf(stderr, "Could not reallocate FIFO\n");
return ret;
}
while (feof(fp_in) == 0)
{
//输入一帧数据的长度
int length = src_nb_samples * av_get_bytes_per_sample(src_fmt) * src_channels;
fread((void *)in_data[0], 1, length, fp_in);
/* compute destination number of samples */
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr, src_rate) + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
int convert_samples = swr_convert(swr, out_data, dst_nb_samples, (const uint8_t **)in_data, src_nb_samples);
int dst_bufsize = av_samples_get_buffer_size(NULL, dst_channels, convert_samples, dst_fmt, 1);
int ret = av_audio_fifo_write(fifo, (void **)out_data, convert_samples);
if ((ret = av_audio_fifo_read(fifo, (void **)frame->data, frame->nb_samples)) < frame->nb_samples) {
fprintf(stderr, "Could not read data from FIFO\n");
return AVERROR_EXIT;
}
encode(codec_ctx, frame, pkt, fp_aacout);
if (av_sample_fmt_is_planar(dst_fmt))
{
//fprintf(stderr, "%s is planar\n", av_get_sample_fmt_name(dst_fmt));
//change planar data into packet data, do "ffplay -ar 44100 -ac 2 -f f32le beijingbeijing_44100_stereo_f32le.pcm"
int dst_fmt_size = av_get_bytes_per_sample(dst_fmt);
for (int i = 0; i < convert_samples; i++)
{
for (int ch = 0; ch < dst_channels; ch++)
{
fwrite(out_data[ch] + dst_fmt_size * i, 1, dst_fmt_size, fp_out);
}
}
}
else
{
fwrite(out_data[0], 1, dst_bufsize, fp_out);
}
fflush(fp_out);
}
while (av_audio_fifo_size(fifo) >= frame->nb_samples)
{
if (av_audio_fifo_read(fifo, (void **)frame->data, frame->nb_samples) < frame->nb_samples)
{
av_log(NULL, AV_LOG_ERROR, "Could not read data from FIFO\n");
return AVERROR_EXIT;
}
encode(codec_ctx, frame, pkt, fp_aacout);
}
encode(codec_ctx, NULL, pkt, fp_aacout);
fclose(fp_out);
fclose(fp_aacout);
fclose(fp_in);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
AVAudioFifo是一个缓冲区,它以一次音频采样为基本单位,是一个先进先出的队列。有了它,对音频的采样数据做缓冲就会变得非常简单,
其优势有两点:
一,它让我们在采样层面做操作,而不是更底层的字节层面;
二,它支持多种格式的单次采样,如支持planar或packed的采样格式,支持不同的通道数等等。
//分配一个AVAudioFifo。
//sample_fmt和channels指定单次采样的参数
//nb_samples则指定AVAudioFifo的缓冲区大小。
AVAudioFifo *av_audio_fifo_alloc(enum AVSampleFormat sample_fmt, int channels,int nb_samples);
//重新分配缓冲区大小
//成功返回0,失败返回负的错误值
int av_audio_fifo_realloc(AVAudioFifo *af, int nb_samples);
//释放AVAudioFifo
void av_audio_fifo_free(AVAudioFifo *af);
//将采样写入到AVAudioFifo
//成功则返回实际写入的采样数(实际上如果写入成功,返回值必定等于nb_samples),失败返回负的错误值
int av_audio_fifo_write(AVAudioFifo *af, void **data, int nb_samples);
//peek:读取数据,但读到的数据并不会从fifo中删除
int av_audio_fifo_peek(AVAudioFifo *af, void **data, int nb_samples);
//从指定的偏移位置peek数据
int av_audio_fifo_peek_at(AVAudioFifo *af, void **data, int nb_samples, int offset);
//读取数据,读到的数据会从fifo中删除
int av_audio_fifo_read(AVAudioFifo *af, void **data, int nb_samples);
//从fifo中删除nb_samples个采样
int av_audio_fifo_drain(AVAudioFifo *af, int nb_samples);
//删除fifo中的所有采样,清空
void av_audio_fifo_reset(AVAudioFifo *af);
//返回fifo中当前存储的采样数量
int av_audio_fifo_size(AVAudioFifo *af);
//返回fifo中当前可写的采样数量,即尚未使用的空间数量
int av_audio_fifo_space(AVAudioFifo *af);
//同一时刻,以上两个函数的返回值之和等于fifo的缓冲区大小