summaryrefslogtreecommitdiff
path: root/src/codec/ffmpeg/resampler.c
blob: fa868c85e98525a54f8d010bcd8d1903e8e2eefa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#define AL_LOG_SECTION "ff_resampler"
#include <al/log.h>
#include <al/lib.h>
#include <libavutil/opt.h>

#include "resampler.h"

static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampler_format *fmt)
{
    struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
    av->resample_context = NULL;

    al_assert(fmt->resampler_needed);
    camu_audio_format_copy(&av->fmt, &fmt->req);

    SwrContext *swr = swr_alloc();

#ifndef CAMU_OLD_FFMPEG
    av_opt_set_chlayout(swr, "in_chlayout", &fmt->in.channel_layout, 0);
    av_opt_set_chlayout(swr, "out_chlayout", &fmt->req.channel_layout, 0);
#else
    av_opt_set_channel_layout(swr, "in_channel_layout", fmt->in.channel_layout, 0);
    av_opt_set_channel_layout(swr, "out_channel_layout", fmt->req.channel_layout, 0);
#endif
    av_opt_set_int(swr, "in_sample_rate", fmt->in.sample_rate, 0);
    av_opt_set_int(swr, "out_sample_rate", fmt->req.sample_rate, 0);
    av_opt_set_sample_fmt(swr, "in_sample_fmt", (enum AVSampleFormat)fmt->in.format, 0);
    av_opt_set_sample_fmt(swr, "out_sample_fmt", (enum AVSampleFormat)fmt->req.format, 0);

#ifdef CAMU_HAVE_SOXR
    // Slower but more accurate resampler.
    av_opt_set_int(swr, "resampler", SWR_ENGINE_SOXR, 0);
    log_info("Using SoX resampler.");
#endif

    s32 ret = swr_init(swr);
    if (ret != 0) {
        swr_free(&swr);
        log_error("Failed to init resampler context (%s).", av_err2str(ret));
        return false;
    }

    av->resample_context = swr;

    return true;
}

static inline void free_sample_data(struct camu_ff_resampler *av)
{
    if (av->data[0]) av_freep(&av->data[0]);
}

static inline void alloc_sample_data(struct camu_ff_resampler *av, s32 sample_count)
{
    if (av->sample_count >= sample_count) return;
    free_sample_data(av);
    s32 nb_channels = av->fmt.channel_count;
    av_samples_alloc(av->data, NULL, nb_channels, sample_count, (enum AVSampleFormat)av->fmt.format, 32);
    av->sample_count = sample_count;
}

static s32 ff_resampler_convert(struct camu_resampler *resamp, const u8 **in_data, s32 in_samples)
{
    struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
    al_assert(in_samples > 0);
    s32 out_resample_count = swr_get_out_samples(av->resample_context, in_samples);
    alloc_sample_data(av, out_resample_count);
    s32 resample_count = swr_convert(av->resample_context, av->data,
        out_resample_count, in_data, in_samples);
    if (resample_count < 0) {
        log_error("Failed to convert samples (%d).", resample_count);
    }
    return resample_count;
}

static s32 ff_resampler_flush(struct camu_resampler *resamp)
{
    struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
    s32 resample_count = swr_convert(av->resample_context, av->data,
        av->sample_count, NULL, 0);
    if (swr_init(av->resample_context) != 0) {
        swr_free(&av->resample_context);
        log_error("Failed to re-init resampler context after flush.");
        return -1;
    }
    return resample_count;
}

static u8 **ff_resampler_get_data(struct camu_resampler *resamp)
{
    struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
    return (u8 **)av->data;
}

static void ff_resampler_free(struct camu_resampler **resamp)
{
    struct camu_ff_resampler *av = (struct camu_ff_resampler *)*resamp;
    if (av->resample_context) {
        swr_close(av->resample_context);
        swr_free(&av->resample_context);
    }
    free_sample_data(av);
    al_free(av);
    *resamp = NULL;
}

struct camu_resampler *camu_ff_resampler_create(void)
{
    struct camu_ff_resampler *av = al_alloc_object(struct camu_ff_resampler);
    av->resamp.init = ff_resampler_init;
    av->resamp.convert = ff_resampler_convert;
    av->resamp.flush = ff_resampler_flush;
    av->resamp.get_data = ff_resampler_get_data;
    av->resamp.free = ff_resampler_free;
    return (struct camu_resampler *)av;
}