]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/resampler.c
resampler: Implement leftover handling in convert_to_work_format()
[pulseaudio] / src / pulsecore / resampler.c
index 7754ef8826041663ad2fda80c05a9dc9623313b3..db65ceaad164037058afeb0b44630eefbb91ebd2 100644 (file)
@@ -60,11 +60,19 @@ struct pa_resampler {
     pa_memchunk remap_buf;
     pa_memchunk resample_buf;
     pa_memchunk from_work_format_buf;
-    unsigned to_work_format_buf_samples;
+    size_t to_work_format_buf_size;
     size_t remap_buf_size;
-    unsigned resample_buf_samples;
-    unsigned from_work_format_buf_samples;
-    bool remap_buf_contains_leftover_data;
+    size_t resample_buf_size;
+    size_t from_work_format_buf_size;
+
+    /* points to buffer before resampling stage, remap or to_work */
+    pa_memchunk *leftover_buf;
+    size_t *leftover_buf_size;
+
+    /* have_leftover points to leftover_in_remap or leftover_in_to_work */
+    bool *have_leftover;
+    bool leftover_in_remap;
+    bool leftover_in_to_work;
 
     pa_sample_format_t work_format;
     uint8_t work_channels;
@@ -93,7 +101,6 @@ struct peaks_data { /* data specific to the peak finder pseudo resampler */
 
 struct ffmpeg_data { /* data specific to ffmpeg */
     struct AVResampleContext *state;
-    pa_memchunk buf[PA_CHANNELS_MAX];
 };
 
 static int copy_init(pa_resampler *r);
@@ -196,8 +203,8 @@ static pa_resample_method_t pa_resampler_fix_method(
                 const uint32_t rate_a,
                 const uint32_t rate_b) {
 
-    pa_assert(rate_a > 0 && rate_a <= PA_RATE_MAX);
-    pa_assert(rate_b > 0 && rate_b <= PA_RATE_MAX);
+    pa_assert(pa_sample_rate_valid(rate_a));
+    pa_assert(pa_sample_rate_valid(rate_b));
     pa_assert(method >= 0);
     pa_assert(method < PA_RESAMPLER_MAX);
 
@@ -247,8 +254,8 @@ static pa_resample_method_t pa_resampler_fix_method(
 
 /* Return true if a is a more precise sample format than b, else return false */
 static bool sample_format_more_precise(pa_sample_format_t a, pa_sample_format_t b) {
-    pa_assert(a >= 0 && a < PA_SAMPLE_MAX);
-    pa_assert(b >= 0 && b < PA_SAMPLE_MAX);
+    pa_assert(pa_sample_format_valid(a));
+    pa_assert(pa_sample_format_valid(b));
 
     switch (a) {
         case PA_SAMPLE_U8:
@@ -299,8 +306,8 @@ static pa_sample_format_t pa_resampler_choose_work_format(
                     bool map_required) {
     pa_sample_format_t work_format;
 
-    pa_assert(a >= 0 && a < PA_SAMPLE_MAX);
-    pa_assert(b >= 0 && b < PA_SAMPLE_MAX);
+    pa_assert(pa_sample_format_valid(a));
+    pa_assert(pa_sample_format_valid(b));
     pa_assert(method >= 0);
     pa_assert(method < PA_RESAMPLER_MAX);
 
@@ -421,6 +428,11 @@ pa_resampler* pa_resampler_new(
         }
     }
 
+    /* leftover buffer is the buffer before the resampling stage */
+    r->leftover_buf = &r->remap_buf;
+    r->leftover_buf_size = &r->remap_buf_size;
+    r->have_leftover = &r->leftover_in_remap;
+
     r->work_channels = r->o_ss.channels;
     r->w_fz = pa_sample_size_of_format(r->work_format) * r->work_channels;
 
@@ -506,9 +518,8 @@ size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
      * enough output buffer. */
 
     frames = (in_length + r->i_fz - 1) / r->i_fz;
-
-    if (r->remap_buf_contains_leftover_data)
-        frames += r->remap_buf.length / r->w_fz;
+    if (*r->have_leftover)
+        frames += r->leftover_buf->length / r->w_fz;
 
     return (((uint64_t) frames * r->o_ss.rate + r->i_ss.rate - 1) / r->i_ss.rate) * r->o_fz;
 }
@@ -536,8 +547,9 @@ size_t pa_resampler_max_block_size(pa_resampler *r) {
     max_fs = pa_frame_size(&max_ss);
     frames = block_size_max / max_fs - EXTRA_FRAMES;
 
-    if (r->remap_buf_contains_leftover_data)
-        frames -= r->remap_buf.length / r->w_fz;
+    pa_assert(frames >= (r->leftover_buf->length / r->w_fz));
+    if (*r->have_leftover)
+        frames -= r->leftover_buf->length / r->w_fz;
 
     block_size_max = ((uint64_t) frames * r->i_ss.rate / max_ss.rate) * r->i_fz;
 
@@ -564,7 +576,7 @@ void pa_resampler_reset(pa_resampler *r) {
     if (r->impl.reset)
         r->impl.reset(r);
 
-    r->remap_buf_contains_leftover_data = false;
+    *r->have_leftover = false;
 }
 
 pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
@@ -1129,37 +1141,72 @@ static void calc_map_table(pa_resampler *r) {
     pa_init_remap(m);
 }
 
+/* check if buf's memblock is large enough to hold 'len' bytes; create a
+ * new memblock if necessary and optionally preserve 'copy' data bytes */
+static void fit_buf(pa_resampler *r, pa_memchunk *buf, size_t len, size_t *size, size_t copy) {
+    pa_assert(size);
+
+    if (!buf->memblock || len > *size) {
+        pa_memblock *new_block = pa_memblock_new(r->mempool, len);
+
+        if (buf->memblock) {
+            if (copy > 0) {
+                void *src = pa_memblock_acquire(buf->memblock);
+                void *dst = pa_memblock_acquire(new_block);
+                pa_assert(copy <= len);
+                memcpy(dst, src, copy);
+                pa_memblock_release(new_block);
+                pa_memblock_release(buf->memblock);
+            }
+
+            pa_memblock_unref(buf->memblock);
+        }
+
+        buf->memblock = new_block;
+        *size = len;
+    }
+
+    buf->length = len;
+}
+
 static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
-    unsigned n_samples;
+    unsigned in_n_samples, out_n_samples;
     void *src, *dst;
+    bool have_leftover;
+    size_t leftover_length = 0;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(input->memblock);
 
     /* Convert the incoming sample into the work sample format and place them
-     * in to_work_format_buf. */
-
-    if (!r->to_work_format_func || !input->length)
-        return input;
+     * in to_work_format_buf. The leftover data is already converted, so it's
+     * part of the output buffer. */
 
-    n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
+    have_leftover = r->leftover_in_to_work;
+    r->leftover_in_to_work = false;
 
-    r->to_work_format_buf.index = 0;
-    r->to_work_format_buf.length = r->w_sz * n_samples;
+    if (!have_leftover && (!r->to_work_format_func || !input->length))
+        return input;
+    else if (input->length <= 0)
+        return &r->to_work_format_buf;
 
-    if (!r->to_work_format_buf.memblock || r->to_work_format_buf_samples < n_samples) {
-        if (r->to_work_format_buf.memblock)
-            pa_memblock_unref(r->to_work_format_buf.memblock);
+    in_n_samples = out_n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
 
-        r->to_work_format_buf_samples = n_samples;
-        r->to_work_format_buf.memblock = pa_memblock_new(r->mempool, r->to_work_format_buf.length);
+    if (have_leftover) {
+        leftover_length = r->to_work_format_buf.length;
+        out_n_samples += (unsigned) (leftover_length / r->w_sz);
     }
 
+    fit_buf(r, &r->to_work_format_buf, r->w_sz * out_n_samples, &r->to_work_format_buf_size, leftover_length);
+
     src = pa_memblock_acquire_chunk(input);
-    dst = pa_memblock_acquire(r->to_work_format_buf.memblock);
+    dst = (uint8_t *) pa_memblock_acquire(r->to_work_format_buf.memblock) + leftover_length;
 
-    r->to_work_format_func(n_samples, src, dst);
+    if (r->to_work_format_func)
+        r->to_work_format_func(in_n_samples, src, dst);
+    else
+        memcpy(dst, src, input->length);
 
     pa_memblock_release(input->memblock);
     pa_memblock_release(r->to_work_format_buf.memblock);
@@ -1181,8 +1228,8 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
      * data in the beginning of remap_buf. The leftover data is already
      * remapped, so it's not part of the input, it's part of the output. */
 
-    have_leftover = r->remap_buf_contains_leftover_data;
-    r->remap_buf_contains_leftover_data = false;
+    have_leftover = r->leftover_in_remap;
+    r->leftover_in_remap = false;
 
     if (!have_leftover && (!r->map_required || input->length <= 0))
         return input;
@@ -1198,32 +1245,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
     }
 
     out_n_samples = out_n_frames * r->o_ss.channels;
-    r->remap_buf.length = out_n_samples * r->w_sz;
-
-    if (have_leftover) {
-        if (r->remap_buf_size < r->remap_buf.length) {
-            pa_memblock *new_block = pa_memblock_new(r->mempool, r->remap_buf.length);
-
-            src = pa_memblock_acquire(r->remap_buf.memblock);
-            dst = pa_memblock_acquire(new_block);
-            memcpy(dst, src, leftover_length);
-            pa_memblock_release(r->remap_buf.memblock);
-            pa_memblock_release(new_block);
-
-            pa_memblock_unref(r->remap_buf.memblock);
-            r->remap_buf.memblock = new_block;
-            r->remap_buf_size = r->remap_buf.length;
-        }
-
-    } else {
-        if (!r->remap_buf.memblock || r->remap_buf_size < r->remap_buf.length) {
-            if (r->remap_buf.memblock)
-                pa_memblock_unref(r->remap_buf.memblock);
-
-            r->remap_buf_size = r->remap_buf.length;
-            r->remap_buf.memblock = pa_memblock_new(r->mempool, r->remap_buf.length);
-        }
-    }
+    fit_buf(r, &r->remap_buf, out_n_samples * r->w_sz, &r->remap_buf_size, leftover_length);
 
     src = pa_memblock_acquire_chunk(input);
     dst = (uint8_t *) pa_memblock_acquire(r->remap_buf.memblock) + leftover_length;
@@ -1250,29 +1272,17 @@ static void save_leftover(pa_resampler *r, void *buf, size_t len) {
     pa_assert(buf);
     pa_assert(len > 0);
 
-    /* Store the leftover to remap_buf. */
-
-    r->remap_buf.length = len;
-
-    if (!r->remap_buf.memblock || r->remap_buf_size < r->remap_buf.length) {
-        if (r->remap_buf.memblock)
-            pa_memblock_unref(r->remap_buf.memblock);
-
-        r->remap_buf_size = r->remap_buf.length;
-        r->remap_buf.memblock = pa_memblock_new(r->mempool, r->remap_buf.length);
-    }
-
-    dst = pa_memblock_acquire(r->remap_buf.memblock);
-    memcpy(dst, buf, r->remap_buf.length);
-    pa_memblock_release(r->remap_buf.memblock);
+    /* Store the leftover data. */
+    fit_buf(r, r->leftover_buf, len, r->leftover_buf_size, 0);
+    *r->have_leftover = true;
 
-    r->remap_buf_contains_leftover_data = true;
+    dst = pa_memblock_acquire(r->leftover_buf->memblock);
+    memmove(dst, buf, len);
+    pa_memblock_release(r->leftover_buf->memblock);
 }
 
 static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
-    unsigned in_n_frames, in_n_samples;
-    unsigned out_n_frames, out_n_samples;
-    unsigned leftover_n_frames;
+    unsigned in_n_frames, out_n_frames, leftover_n_frames;
 
     pa_assert(r);
     pa_assert(input);
@@ -1282,22 +1292,10 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
     if (!r->impl.resample || !input->length)
         return input;
 
-    in_n_samples = (unsigned) (input->length / r->w_sz);
-    in_n_frames = (unsigned) (in_n_samples / r->work_channels);
+    in_n_frames = (unsigned) (input->length / r->w_fz);
 
     out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
-    out_n_samples = out_n_frames * r->work_channels;
-
-    r->resample_buf.index = 0;
-    r->resample_buf.length = r->w_sz * out_n_samples;
-
-    if (!r->resample_buf.memblock || r->resample_buf_samples < out_n_samples) {
-        if (r->resample_buf.memblock)
-            pa_memblock_unref(r->resample_buf.memblock);
-
-        r->resample_buf_samples = out_n_samples;
-        r->resample_buf.memblock = pa_memblock_new(r->mempool, r->resample_buf.length);
-    }
+    fit_buf(r, &r->resample_buf, r->w_fz * out_n_frames, &r->resample_buf_size, 0);
 
     leftover_n_frames = r->impl.resample(r, input, in_n_frames, &r->resample_buf, &out_n_frames);
 
@@ -1327,17 +1325,7 @@ static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input
 
     n_samples = (unsigned) (input->length / r->w_sz);
     n_frames = n_samples / r->o_ss.channels;
-
-    r->from_work_format_buf.index = 0;
-    r->from_work_format_buf.length = r->o_fz * n_frames;
-
-    if (!r->from_work_format_buf.memblock || r->from_work_format_buf_samples < n_samples) {
-        if (r->from_work_format_buf.memblock)
-            pa_memblock_unref(r->from_work_format_buf.memblock);
-
-        r->from_work_format_buf_samples = n_samples;
-        r->from_work_format_buf.memblock = pa_memblock_new(r->mempool, r->from_work_format_buf.length);
-    }
+    fit_buf(r, &r->from_work_format_buf, r->o_fz * n_frames, &r->from_work_format_buf_size, 0);
 
     src = pa_memblock_acquire_chunk(input);
     dst = pa_memblock_acquire(r->from_work_format_buf.memblock);
@@ -1790,7 +1778,7 @@ static unsigned ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsig
         int consumed_frames;
 
         /* Allocate a new block */
-        b = pa_memblock_new(r->mempool, ffmpeg_data->buf[c].length + in_n_frames * sizeof(int16_t));
+        b = pa_memblock_new(r->mempool, in_n_frames * sizeof(int16_t));
         p = pa_memblock_acquire(b);
 
         /* Now copy the input data, splitting up channels */
@@ -1839,7 +1827,6 @@ static unsigned ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsig
 }
 
 static void ffmpeg_free(pa_resampler *r) {
-    unsigned c;
     struct ffmpeg_data *ffmpeg_data;
 
     pa_assert(r);
@@ -1847,14 +1834,9 @@ static void ffmpeg_free(pa_resampler *r) {
     ffmpeg_data = r->impl.data;
     if (ffmpeg_data->state)
         av_resample_close(ffmpeg_data->state);
-
-    for (c = 0; c < PA_ELEMENTSOF(ffmpeg_data->buf); c++)
-        if (ffmpeg_data->buf[c].memblock)
-            pa_memblock_unref(ffmpeg_data->buf[c].memblock);
 }
 
 static int ffmpeg_init(pa_resampler *r) {
-    unsigned c;
     struct ffmpeg_data *ffmpeg_data;
 
     pa_assert(r);
@@ -1873,9 +1855,6 @@ static int ffmpeg_init(pa_resampler *r) {
     r->impl.resample = ffmpeg_resample;
     r->impl.data = (void *) ffmpeg_data;
 
-    for (c = 0; c < PA_ELEMENTSOF(ffmpeg_data->buf); c++)
-        pa_memchunk_reset(&ffmpeg_data->buf[c]);
-
     return 0;
 }