| /* | |
| * Copyright (c) 2012 Stefano Sabatini | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| * THE SOFTWARE. | |
| */ | |
| /** | |
| * @file audio resampling API usage example | |
| * @example resample_audio.c | |
| * | |
| * Generate a synthetic audio signal, and Use libswresample API to perform audio | |
| * resampling. The output is written to a raw audio file to be played with | |
| * ffplay. | |
| */ | |
| #include <libavutil/opt.h> | |
| #include <libavutil/channel_layout.h> | |
| #include <libavutil/samplefmt.h> | |
| #include <libswresample/swresample.h> | |
| static int get_format_from_sample_fmt(const char **fmt, | |
| enum AVSampleFormat sample_fmt) | |
| { | |
| int i; | |
| struct sample_fmt_entry { | |
| enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; | |
| } sample_fmt_entries[] = { | |
| { AV_SAMPLE_FMT_U8, "u8", "u8" }, | |
| { AV_SAMPLE_FMT_S16, "s16be", "s16le" }, | |
| { AV_SAMPLE_FMT_S32, "s32be", "s32le" }, | |
| { AV_SAMPLE_FMT_FLT, "f32be", "f32le" }, | |
| { AV_SAMPLE_FMT_DBL, "f64be", "f64le" }, | |
| }; | |
| *fmt = NULL; | |
| for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { | |
| struct sample_fmt_entry *entry = &sample_fmt_entries[i]; | |
| if (sample_fmt == entry->sample_fmt) { | |
| *fmt = AV_NE(entry->fmt_be, entry->fmt_le); | |
| return 0; | |
| } | |
| } | |
| fprintf(stderr, | |
| "Sample format %s not supported as output format\n", | |
| av_get_sample_fmt_name(sample_fmt)); | |
| return AVERROR(EINVAL); | |
| } | |
| /** | |
| * Fill dst buffer with nb_samples, generated starting from t. | |
| */ | |
| static void fill_samples(double *dst, int nb_samples, int nb_channels, int sample_rate, double *t) | |
| { | |
| int i, j; | |
| double tincr = 1.0 / sample_rate, *dstp = dst; | |
| const double c = 2 * M_PI * 440.0; | |
| /* generate sin tone with 440Hz frequency and duplicated channels */ | |
| for (i = 0; i < nb_samples; i++) { | |
| *dstp = sin(c * *t); | |
| for (j = 1; j < nb_channels; j++) | |
| dstp[j] = dstp[0]; | |
| dstp += nb_channels; | |
| *t += tincr; | |
| } | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| AVChannelLayout src_ch_layout = AV_CHANNEL_LAYOUT_STEREO, dst_ch_layout = AV_CHANNEL_LAYOUT_SURROUND; | |
| int src_rate = 48000, dst_rate = 44100; | |
| uint8_t **src_data = NULL, **dst_data = NULL; | |
| int src_nb_channels = 0, dst_nb_channels = 0; | |
| int src_linesize, dst_linesize; | |
| int src_nb_samples = 1024, dst_nb_samples, max_dst_nb_samples; | |
| enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_DBL, dst_sample_fmt = AV_SAMPLE_FMT_S16; | |
| const char *dst_filename = NULL; | |
| FILE *dst_file; | |
| int dst_bufsize; | |
| const char *fmt; | |
| struct SwrContext *swr_ctx; | |
| char buf[64]; | |
| double t; | |
| int ret; | |
| if (argc != 2) { | |
| fprintf(stderr, "Usage: %s output_file\n" | |
| "API example program to show how to resample an audio stream with libswresample.\n" | |
| "This program generates a series of audio frames, resamples them to a specified " | |
| "output format and rate and saves them to an output file named output_file.\n", | |
| argv[0]); | |
| exit(1); | |
| } | |
| dst_filename = argv[1]; | |
| dst_file = fopen(dst_filename, "wb"); | |
| if (!dst_file) { | |
| fprintf(stderr, "Could not open destination file %s\n", dst_filename); | |
| exit(1); | |
| } | |
| /* create resampler context */ | |
| swr_ctx = swr_alloc(); | |
| if (!swr_ctx) { | |
| fprintf(stderr, "Could not allocate resampler context\n"); | |
| ret = AVERROR(ENOMEM); | |
| goto end; | |
| } | |
| /* set options */ | |
| av_opt_set_chlayout(swr_ctx, "in_chlayout", &src_ch_layout, 0); | |
| av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0); | |
| av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0); | |
| av_opt_set_chlayout(swr_ctx, "out_chlayout", &dst_ch_layout, 0); | |
| av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0); | |
| av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0); | |
| /* initialize the resampling context */ | |
| if ((ret = swr_init(swr_ctx)) < 0) { | |
| fprintf(stderr, "Failed to initialize the resampling context\n"); | |
| goto end; | |
| } | |
| /* allocate source and destination samples buffers */ | |
| src_nb_channels = src_ch_layout.nb_channels; | |
| ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels, | |
| src_nb_samples, src_sample_fmt, 0); | |
| if (ret < 0) { | |
| fprintf(stderr, "Could not allocate source samples\n"); | |
| goto end; | |
| } | |
| /* compute the number of converted samples: buffering is avoided | |
| * ensuring that the output buffer will contain at least all the | |
| * converted input samples */ | |
| max_dst_nb_samples = dst_nb_samples = | |
| av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP); | |
| /* buffer is going to be directly written to a rawaudio file, no alignment */ | |
| dst_nb_channels = dst_ch_layout.nb_channels; | |
| ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels, | |
| dst_nb_samples, dst_sample_fmt, 0); | |
| if (ret < 0) { | |
| fprintf(stderr, "Could not allocate destination samples\n"); | |
| goto end; | |
| } | |
| t = 0; | |
| do { | |
| /* generate synthetic audio */ | |
| fill_samples((double *)src_data[0], src_nb_samples, src_nb_channels, src_rate, &t); | |
| /* compute destination number of samples */ | |
| dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, src_rate) + | |
| src_nb_samples, dst_rate, src_rate, AV_ROUND_UP); | |
| if (dst_nb_samples > max_dst_nb_samples) { | |
| av_freep(&dst_data[0]); | |
| ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels, | |
| dst_nb_samples, dst_sample_fmt, 1); | |
| if (ret < 0) | |
| break; | |
| max_dst_nb_samples = dst_nb_samples; | |
| } | |
| /* convert to destination format */ | |
| ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples); | |
| if (ret < 0) { | |
| fprintf(stderr, "Error while converting\n"); | |
| goto end; | |
| } | |
| dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, | |
| ret, dst_sample_fmt, 1); | |
| if (dst_bufsize < 0) { | |
| fprintf(stderr, "Could not get sample buffer size\n"); | |
| goto end; | |
| } | |
| printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret); | |
| fwrite(dst_data[0], 1, dst_bufsize, dst_file); | |
| } while (t < 10); | |
| if ((ret = get_format_from_sample_fmt(&fmt, dst_sample_fmt)) < 0) | |
| goto end; | |
| av_channel_layout_describe(&dst_ch_layout, buf, sizeof(buf)); | |
| fprintf(stderr, "Resampling succeeded. Play the output file with the command:\n" | |
| "ffplay -f %s -channel_layout %s -channels %d -ar %d %s\n", | |
| fmt, buf, dst_nb_channels, dst_rate, dst_filename); | |
| end: | |
| fclose(dst_file); | |
| 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(&swr_ctx); | |
| return ret < 0; | |
| } |