audio.c 8.8 KB
Newer Older
Andy Wingo's avatar
Andy Wingo committed
1
/* GStreamer
2 3 4 5 6 7 8 9 10 11 12 13 14 15
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18
 */
19 20
/**
 * SECTION:gstaudio
21
 * @title: GstAudio
22 23 24 25
 * @short_description: Support library for audio elements
 *
 * This library contains some helper functions for audio elements.
 */
26

27 28 29 30
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

Wim Taymans's avatar
Wim Taymans committed
31 32
#include <string.h>

33
#include "audio.h"
34
#include "audio-enumtypes.h"
35

Wim Taymans's avatar
Wim Taymans committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT ensure_debug_category()
static GstDebugCategory *
ensure_debug_category (void)
{
  static gsize cat_gonce = 0;

  if (g_once_init_enter (&cat_gonce)) {
    gsize cat_done;

    cat_done = (gsize) _gst_debug_category_new ("audio", 0, "audio library");

    g_once_init_leave (&cat_gonce, cat_done);
  }

  return (GstDebugCategory *) cat_gonce;
}
#else
#define ensure_debug_category() /* NOOP */
#endif /* GST_DISABLE_GST_DEBUG */


58 59
/**
 * gst_audio_buffer_clip:
Olivier Crête's avatar
Olivier Crête committed
60
 * @buffer: (transfer full): The buffer to clip.
Wim Taymans's avatar
Wim Taymans committed
61 62
 * @segment: Segment in %GST_FORMAT_TIME or %GST_FORMAT_DEFAULT to which
 *           the buffer should be clipped.
63
 * @rate: sample rate.
64
 * @bpf: size of one audio frame in bytes. This is the size of one sample *
Stefan Sauer's avatar
Stefan Sauer committed
65
 * number of channels.
66
 *
Piotr Fusik's avatar
Piotr Fusik committed
67
 * Clip the buffer to the given %GstSegment.
68
 *
Wim Taymans's avatar
Wim Taymans committed
69
 * After calling this function the caller does not own a reference to
70
 * @buffer anymore.
71
 *
Olivier Crête's avatar
Olivier Crête committed
72
 * Returns: (transfer full): %NULL if the buffer is completely outside the configured segment,
73 74
 * otherwise the clipped buffer is returned.
 *
75
 * If the buffer has no timestamp, it is assumed to be inside the segment and
Wim Taymans's avatar
Wim Taymans committed
76
 * is not clipped
77 78
 */
GstBuffer *
79 80
gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
    gint rate, gint bpf)
81 82
{
  GstBuffer *ret;
83
  GstAudioMeta *meta;
84 85
  GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
  guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE;
86
  gint trim, size, osize;
87 88 89 90 91 92
  gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end =
      TRUE;

  g_return_val_if_fail (segment->format == GST_FORMAT_TIME ||
      segment->format == GST_FORMAT_DEFAULT, buffer);
  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
93 94 95 96

  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
    /* No timestamp - assume the buffer is completely in the segment */
    return buffer;
97

98
  /* Get copies of the buffer metadata to change later.
99 100 101
   * Calculate the missing values for the calculations,
   * they won't be changed later though. */

102 103 104
  meta = gst_buffer_get_audio_meta (buffer);

  /* these variables measure samples */
Wim Taymans's avatar
Wim Taymans committed
105
  trim = 0;
106
  osize = size = meta ? meta->samples : (gst_buffer_get_size (buffer) / bpf);
107

108 109 110 111
  /* no data, nothing to clip */
  if (!size)
    return buffer;

112
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
Wim Taymans's avatar
Wim Taymans committed
113
  GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
114 115 116 117
  if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
    duration = GST_BUFFER_DURATION (buffer);
  } else {
    change_duration = FALSE;
118
    duration = gst_util_uint64_scale (size, GST_SECOND, rate);
119 120 121 122 123 124 125 126 127 128 129 130 131
  }

  if (GST_BUFFER_OFFSET_IS_VALID (buffer)) {
    offset = GST_BUFFER_OFFSET (buffer);
  } else {
    change_offset = FALSE;
    offset = 0;
  }

  if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) {
    offset_end = GST_BUFFER_OFFSET_END (buffer);
  } else {
    change_offset_end = FALSE;
132
    offset_end = offset + size;
133 134 135 136 137
  }

  if (segment->format == GST_FORMAT_TIME) {
    /* Handle clipping for GST_FORMAT_TIME */

Wim Taymans's avatar
Wim Taymans committed
138
    guint64 start, stop, cstart, cstop, diff;
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

    start = timestamp;
    stop = timestamp + duration;

    if (gst_segment_clip (segment, GST_FORMAT_TIME,
            start, stop, &cstart, &cstop)) {

      diff = cstart - start;
      if (diff > 0) {
        timestamp = cstart;

        if (change_duration)
          duration -= diff;

        diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
        if (change_offset)
          offset += diff;
156 157
        trim += diff;
        size -= diff;
158 159 160 161 162 163 164 165 166 167
      }

      diff = stop - cstop;
      if (diff > 0) {
        /* duration is always valid if stop is valid */
        duration -= diff;

        diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
        if (change_offset_end)
          offset_end -= diff;
168
        size -= diff;
169 170 171 172 173 174 175
      }
    } else {
      gst_buffer_unref (buffer);
      return NULL;
    }
  } else {
    /* Handle clipping for GST_FORMAT_DEFAULT */
Wim Taymans's avatar
Wim Taymans committed
176
    guint64 start, stop, cstart, cstop, diff;
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194

    g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer);

    start = offset;
    stop = offset_end;

    if (gst_segment_clip (segment, GST_FORMAT_DEFAULT,
            start, stop, &cstart, &cstop)) {

      diff = cstart - start;
      if (diff > 0) {
        offset = cstart;

        timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate);

        if (change_duration)
          duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);

195 196
        trim += diff;
        size -= diff;
197 198 199 200 201 202 203 204 205
      }

      diff = stop - cstop;
      if (diff > 0) {
        offset_end = cstop;

        if (change_duration)
          duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);

206
        size -= diff;
207 208 209 210 211 212 213
      }
    } else {
      gst_buffer_unref (buffer);
      return NULL;
    }
  }

214 215
  if (trim == 0 && size == osize) {
    ret = buffer;
216 217 218 219 220 221 222 223 224

    if (GST_BUFFER_TIMESTAMP (ret) != timestamp) {
      ret = gst_buffer_make_writable (ret);
      GST_BUFFER_TIMESTAMP (ret) = timestamp;
    }
    if (GST_BUFFER_DURATION (ret) != duration) {
      ret = gst_buffer_make_writable (ret);
      GST_BUFFER_DURATION (ret) = duration;
    }
225
  } else {
226 227 228
    /* cut out all the samples that are no longer relevant */
    GST_DEBUG ("trim %d, size %d", trim, size);
    ret = gst_audio_buffer_truncate (buffer, bpf, trim, size);
229 230

    GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
Stefan Sauer's avatar
Stefan Sauer committed
231 232
    if (ret) {
      GST_BUFFER_TIMESTAMP (ret) = timestamp;
233

Stefan Sauer's avatar
Stefan Sauer committed
234 235 236 237 238 239 240
      if (change_duration)
        GST_BUFFER_DURATION (ret) = duration;
      if (change_offset)
        GST_BUFFER_OFFSET (ret) = offset;
      if (change_offset_end)
        GST_BUFFER_OFFSET_END (ret) = offset_end;
    } else {
241
      GST_ERROR ("gst_audio_buffer_truncate failed");
Stefan Sauer's avatar
Stefan Sauer committed
242
    }
243
  }
244 245
  return ret;
}
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312

/**
 * gst_audio_buffer_truncate:
 * @buffer: (transfer full): The buffer to truncate.
 * @bpf: size of one audio frame in bytes. This is the size of one sample *
 * number of channels.
 * @trim: the number of samples to remove from the beginning of the buffer
 * @samples: the final number of samples that should exist in this buffer or -1
 * to use all the remaining samples if you are only removing samples from the
 * beginning.
 *
 * Truncate the buffer to finally have @samples number of samples, removing
 * the necessary amount of samples from the end and @trim number of samples
 * from the beginning.
 *
 * After calling this function the caller does not own a reference to
 * @buffer anymore.
 *
 * Returns: (transfer full): the truncated buffer or %NULL if the arguments
 *   were invalid
 *
 * Since: UNRELEASED
 */
GstBuffer *
gst_audio_buffer_truncate (GstBuffer * buffer, gint bpf, gint trim,
    gint samples)
{
  GstAudioMeta *meta = NULL;
  GstBuffer *ret = NULL;
  gint orig_samples, i;

  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
  g_return_val_if_fail (trim >= 0, NULL);

  meta = gst_buffer_get_audio_meta (buffer);
  orig_samples = meta ? meta->samples : gst_buffer_get_size (buffer) / bpf;

  g_return_val_if_fail (trim < orig_samples, NULL);
  g_return_val_if_fail (samples == -1 || trim + samples <= orig_samples, NULL);

  if (samples == -1)
    samples = orig_samples - trim;

  /* nothing to truncate */
  if (samples == orig_samples)
    return buffer;

  if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
    /* interleaved */
    ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim * bpf,
        samples * bpf);
    gst_buffer_unref (buffer);

    if ((meta = gst_buffer_get_audio_meta (ret)))
      meta->samples = samples;
  } else {
    /* non-interleaved */
    ret = gst_buffer_make_writable (buffer);
    meta = gst_buffer_get_audio_meta (buffer);
    meta->samples = samples;
    for (i = 0; i < meta->channels; i++) {
      meta->offsets[i] += trim * bpf / meta->channels;
    }
  }

  return ret;
}