音频重采样

来源:互联网 发布:java 两个类互相引用 编辑:程序博客网 时间:2024/06/05 04:44
 

ffmepg音频重采样

标签: ffmpeg音频处理 音频重采样cc++
 1481人阅读 评论(0) 收藏 举报
 分类:

目录(?)[-]

  1. 概述
  2. 基本概念
    1. 1通道数
    2. 2采样率
    3. 3比特率bps或kbps
    4. 4帧
    5. 5样本格式
  3. ffmepg实例
    1. 1 根据样本格式返回样本格式字符串
    2. 2获取声音
    3. 2主函数

1.概述


在进行音频播放时,有时视频流不能满足播放要求,需要对声音的相关属性如:通道数,采样率,样本存储方式进行变更播放,也就是音频重采样。ffmpeg提供了SwrContext进行转换。

[cpp] view plain copy
print?
  1. typedef struct SwrContext SwrContext;  

2.基本概念


2.1通道数


声音在录制时在不同空间位置用不同录音设备采样的声音信号,声音在播放时采用相应个数的扬声器播放。采用多通道的方式是为了丰富声音的现场感。常用的立体声有2个通道,环绕立体声3个通道。数字音频就是有一连串的样本流组成,立体声每次采用要采两次。有点类似视频中的YUV各个分量。


2.2采样率


把模拟信号转换成数字信号在计算机中处理,需要按照一定的采样率采样,样本值就是声音波形中的一个值。音频在播放时按照采样率进行,采样率越高声音的连续性就越好,由于人的听觉器官分辨能力的局限,往往这些数值达到某种程度就可以满足人对“连续”性的需求了。例如22050和44100的采样率就是电台和CD 常用的采样率。类似视频中的帧率。


2.3比特率(bps或kbps)


单位时间所需的空间存储。比特率反应的是视频或者音频一个样本所有的信息量,越大含有的信息量就大。视频中,图像分辨率越大,一帧就越大,实时解码就容易饥渴,传输带宽需要越大,存储空间就越大。音频中描述一个样本就越准确。


2.4帧


视频中帧就是一个图片采样。音频中一帧一般包含多个样本,如AAC格式会包含1024个样本。


2.5样本格式


音频中一个样本存储方式。列举ffmpeg中的样本格式

[cpp] view plain copy
 print?
  1. enum AVSampleFormat {  
  2.     AV_SAMPLE_FMT_NONE = -1,  
  3.     AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits  
  4.     AV_SAMPLE_FMT_S16,         ///< signed 16 bits  
  5.     AV_SAMPLE_FMT_S32,         ///< signed 32 bits  
  6.     AV_SAMPLE_FMT_FLT,         ///< float  
  7.     AV_SAMPLE_FMT_DBL,         ///< double  
  8.   
  9.     AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar  
  10.     AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar  
  11.     AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar  
  12.     AV_SAMPLE_FMT_FLTP,        ///< float, planar  
  13.     AV_SAMPLE_FMT_DBLP,        ///< double, planar  
  14.   
  15.     AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically  
  16. };  

讲一下AV_SAMPLE_FMT_S16和AV_SAMPLE_FMT_S16P格式,AV_SAMPLE_FMT_S16保存一个样本采用有符号16bit交叉存储的方式,AV_SAMPLE_FMT_S16P保存一个样本采用有符号16bit平面存储的方式。举例有两个通道,通道1数据流 c1 c1 c1c1... , 通道2数据流 c2 c2 c2 c2...

平面存储方式:c1 c1 c1c1... c2 c2 c2 c2...

交叉存储方式:c1, c2,c1, c2, c1, c2, ...

AVFrame中平面方式planar每个通道数据存储在data[0], data[1]中,长度为linesize[0],linesize[1],交叉方式则所有的数据都存储在data[0],长度为linesize[0]。


3.ffmepg实例


例程是ffmpeg2.4源代码目录下的doc/examples/resampling_audio.c文件,为便于学习做部分修改。


3.1 根据样本格式返回样本格式字符串


[cpp] view plain copy
 print?
  1.  static int get_format_from_sample_fmt(const char **fmt,  
  2.     enum AVSampleFormat sample_fmt)  
  3. {  
  4.     int i;  
  5.     struct sample_fmt_entry   
  6.     {  
  7.         enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le;  
  8.     } sample_fmt_entries[] =   
  9.     {  
  10.         { AV_SAMPLE_FMT_U8,  "u8",    "u8"    },  
  11.         { AV_SAMPLE_FMT_S16, "s16be""s16le" },  
  12.         { AV_SAMPLE_FMT_S32, "s32be""s32le" },  
  13.         { AV_SAMPLE_FMT_FLT, "f32be""f32le" },  
  14.         { AV_SAMPLE_FMT_DBL, "f64be""f64le" },  
  15.     };  
  16.     *fmt = NULL;  
  17.   
  18.     for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++)   
  19.     {  
  20.         struct sample_fmt_entry *entry = &sample_fmt_entries[i];  
  21.         if (sample_fmt == entry->sample_fmt)   
  22.         {  
  23.             *fmt = AV_NE(entry->fmt_be, entry->fmt_le);  
  24.             return 0;  
  25.         }  
  26.     }  
  27.   
  28.     fprintf(stderr,  
  29.         "Sample format %s not supported as output format\n",  
  30.         av_get_sample_fmt_name(sample_fmt));  
  31.     return AVERROR(EINVAL);  
  32. }  

 


3.2获取声音


[cpp] view plain copy
 print?
  1. /** 
  2. * Fill dst buffer with nb_samples, generated starting from t. 
  3. *相当于是声源 产生一个正弦波形的声波 
  4. * dst 保存声音数据返回个调用者 nb_samples 采用的样本数 nb_channels 声音通道数,表明单次采样的样本数 t采用开始时间 
  5. *正弦波形就是一个生源,实际中复杂的声音都是通过波形叠加成的。 
  6. *以 sample_rate采样率,从时间t开始采样,采样通道为2,每个通道的数据相同,从频率为440HZ的波形上采样,形成声源 
  7. */  
  8. static void fill_samples(double *dst, int nb_samples, int nb_channels, int sample_rate, double *t)  
  9. {  
  10.     int i, j;  
  11.     //采样时间间隔    tincr  
  12.     double tincr = 1.0 / sample_rate, *dstp = dst;  
  13.     //正弦波y=Asin(ωx+φ)+h 最小正周期T=2π/|ω| 所以440HZ是正弦波的频率  
  14.     const double c = 2 * M_PI * 440.0;  
  15.   
  16.     /* generate sin tone with 440Hz frequency and duplicated channels */  
  17.     //填充每个通道数据 采用交叉存储  
  18.     for (i = 0; i < nb_samples; i++)   
  19.     {  
  20.         *dstp = sin(c * *t);  
  21.         for (j = 1; j < nb_channels; j++)  
  22.         {  
  23.             dstp[j] = dstp[0];  
  24.         }  
  25.         dstp += nb_channels;  
  26.         *t += tincr;  
  27.     }  
  28. }  

3.2主函数


[cpp] view plain copy
 print?
  1. int main(int argc, char **argv)  
  2. {  
  3.     // AV_CH_LAYOUT_STEREO 声音布局立体声    AV_CH_LAYOUT_SURROUND 声音布局环绕立体声  
  4.     int64_t src_ch_layout = AV_CH_LAYOUT_STEREO, dst_ch_layout = AV_CH_LAYOUT_SURROUND;  
  5.     //声音采样率  
  6.     int src_rate = 48000, dst_rate = 44100;  
  7.     uint8_t **src_data = NULL, **dst_data = NULL;  
  8.     int src_nb_channels = 0, dst_nb_channels = 0;  
  9.     int src_linesize, dst_linesize;  
  10.     //每次采用样本数  
  11.     int src_nb_samples = 1024, dst_nb_samples, max_dst_nb_samples;  
  12.     //样本存储格式  
  13.     enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_DBL, dst_sample_fmt = AV_SAMPLE_FMT_S16;  
  14.     const char *dst_filename = NULL;  
  15.     FILE *dst_file;  
  16.     int dst_bufsize;  
  17.     const char *fmt;  
  18.     //重采样上下文  
  19.     struct SwrContext *swr_ctx;  
  20.     double t;  
  21.     int ret;  
  22.   
  23.     if (argc != 2)   
  24.     {  
  25.         fprintf(stderr, "Usage: %s output_file\n"  
  26.             "API example program to show how to resample an audio stream with libswresample.\n"  
  27.             "This program generates a series of audio frames, resamples them to a specified "  
  28.             "output format and rate and saves them to an output file named output_file.\n",  
  29.             argv[0]);  
  30.         exit(1);  
  31.     }  
  32.     dst_filename = argv[1];  
  33.   
  34.     dst_file = fopen(dst_filename, "wb");  
  35.     if (!dst_file)   
  36.     {  
  37.         fprintf(stderr, "Could not open destination file %s\n", dst_filename);  
  38.         exit(1);  
  39.     }  
  40.   
  41.     /* create resampler context */  
  42.     //初始化常采样上下文  
  43.     swr_ctx = swr_alloc();  
  44.     if (!swr_ctx)   
  45.     {  
  46.         fprintf(stderr, "Could not allocate resampler context\n");  
  47.         ret = AVERROR(ENOMEM);  
  48.         goto end;  
  49.     }  
  50.   
  51.     /* set options */  
  52.     //设置源通道布局  
  53.     av_opt_set_int(swr_ctx, "in_channel_layout",    src_ch_layout, 0);  
  54.     //设置源通道采样率  
  55.     av_opt_set_int(swr_ctx, "in_sample_rate",       src_rate, 0);  
  56.     //设置源通道样本格式  
  57.     av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);  
  58.   
  59.     //目标通道布局  
  60.     av_opt_set_int(swr_ctx, "out_channel_layout",    dst_ch_layout, 0);  
  61.     //目标采用率  
  62.     av_opt_set_int(swr_ctx, "out_sample_rate",       dst_rate, 0);  
  63.     //目标样本格式  
  64.     av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);  
  65.   
  66.     /* initialize the resampling context */  
  67.     if ((ret = swr_init(swr_ctx)) < 0)   
  68.     {  
  69.         fprintf(stderr, "Failed to initialize the resampling context\n");  
  70.         goto end;  
  71.     }  
  72.   
  73.     /* allocate source and destination samples buffers */  
  74.     //获取源通道数  
  75.     src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);  
  76.     //分配源声音所需要空间  src_linesize=  src_nb_channels× src_nb_samples×sizeof(double)  
  77.     ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels,  
  78.         src_nb_samples, src_sample_fmt, 0);  
  79.     if (ret < 0)   
  80.     {  
  81.         fprintf(stderr, "Could not allocate source samples\n");  
  82.         goto end;  
  83.     }  
  84.   
  85.     /* compute the number of converted samples: buffering is avoided 
  86.     * ensuring that the output buffer will contain at least all the 
  87.     * converted input samples */  
  88.     //计算目标样本数  转换前后的样本数不一样  抓住一点 采样时间相等  
  89.     //src_nb_samples/src_rate=dst_nb_samples/dst_rate  
  90.     max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);  
  91.   
  92.     /* buffer is going to be directly written to a rawaudio file, no alignment */  
  93.     dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);  
  94.     ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels,  
  95.         dst_nb_samples, dst_sample_fmt, 0);  
  96.     if (ret < 0)   
  97.     {  
  98.         fprintf(stderr, "Could not allocate destination samples\n");  
  99.         goto end;  
  100.     }  
  101.   
  102.     t = 0;  
  103.     do {  
  104.         /* generate synthetic audio */  
  105.         fill_samples((double *)src_data[0], src_nb_samples, src_nb_channels, src_rate, &t);  
  106.   
  107.         /* compute destination number of samples */  
  108.         //swr_get_delay(swr_ctx, src_rate)延迟时间 源采样率为单位的样本数  
  109.         dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, src_rate) +  
  110.             src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);  
  111.         if (dst_nb_samples > max_dst_nb_samples)   
  112.         {  
  113.             av_freep(&dst_data[0]);  
  114.             ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,  
  115.                 dst_nb_samples, dst_sample_fmt, 1);  
  116.             if (ret < 0)  
  117.                 break;  
  118.             max_dst_nb_samples = dst_nb_samples;  
  119.         }  
  120.   
  121.         /* convert to destination format */  
  122.         //ret 实际转换得到的样本数  
  123.         ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);  
  124.         if (ret < 0)   
  125.         {  
  126.             fprintf(stderr, "Error while converting\n");  
  127.             goto end;  
  128.         }  
  129.         dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,  
  130.             ret, dst_sample_fmt, 1);  
  131.         if (dst_bufsize < 0)   
  132.         {  
  133.             fprintf(stderr, "Could not get sample buffer size\n");  
  134.             goto end;  
  135.         }  
  136.         printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret);  
  137.         fwrite(dst_data[0], 1, dst_bufsize, dst_file);  
  138.     } while (t < 10);  
  139.   
  140.     if ((ret = get_format_from_sample_fmt(&fmt, dst_sample_fmt)) < 0)  
  141.         goto end;  
  142.   
  143.     fprintf(stderr, "Resampling succeeded. Play the output file with the command:\n"  
  144.         "ffplay -f %s -channel_layout %lld -channels %d -ar %d %s\n",  
  145.         fmt, dst_ch_layout, dst_nb_channels, dst_rate, dst_filename);  
  146.     while(1)  
  147.     {  
  148.         Sleep(50);  
  149.     }  
  150.   
  151. end:  
  152.     fclose(dst_file);  
  153.   
  154.     if (src_data)  
  155.         av_freep(&src_data[0]);  
  156.     av_freep(&src_data);  
  157.   
  158.     if (dst_data)  
  159.         av_freep(&dst_data[0]);  
  160.     av_freep(&dst_data);  
  161.   
  162.     swr_free(&swr_ctx);  
  163.     return ret < 0;  
  164. }  


编译环境:Win7_32bit+VS2010

FFMPEG版本:ffmpeg-2.4

源代码下载地址:http://download.csdn.net/detail/hiwubihe/9593005

技术在于交流、沟通,转载请注明出处并保持作品的完整性。
原文://http://blog.csdn.net/hiwubihe/article/details/52059378