summaryrefslogtreecommitdiff
blob: 46a28fd3df24128ce6121bd16a6aa316028797f2 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
--- gst/audioresample/gstaudioresample.c	2006/10/28 16:00:51	1.22
+++ gst/audioresample/gstaudioresample.c	2007/03/15 10:52:21	1.25
@@ -194,6 +194,8 @@
   gst_pad_set_bufferalloc_function (trans->sinkpad, NULL);
 
   audioresample->filter_length = DEFAULT_FILTERLEN;
+
+  audioresample->need_discont = FALSE;
 }
 
 /* vmethods */
@@ -371,7 +373,7 @@
   gboolean use_internal = FALSE;        /* whether we use the internal state */
   gboolean ret = TRUE;
 
-  GST_DEBUG_OBJECT (base, "asked to transform size %d in direction %s",
+  GST_LOG_OBJECT (base, "asked to transform size %d in direction %s",
       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
   if (direction == GST_PAD_SINK) {
     sinkcaps = caps;
@@ -406,7 +408,7 @@
 
   /* we make room for one extra sample, given that the resampling filter
    * can output an extra one for non-integral i_rate/o_rate */
-  GST_DEBUG_OBJECT (base, "transformed size %d to %d", size, *othersize);
+  GST_LOG_OBJECT (base, "transformed size %d to %d", size, *othersize);
 
   if (!use_internal) {
     resample_free (state);
@@ -492,8 +494,7 @@
   r = audioresample->resample;
 
   outsize = resample_get_output_size (r);
-  GST_DEBUG_OBJECT (audioresample, "audioresample can give me %d bytes",
-      outsize);
+  GST_LOG_OBJECT (audioresample, "audioresample can give me %d bytes", outsize);
 
   /* protect against mem corruption */
   if (outsize > GST_BUFFER_SIZE (outbuf)) {
@@ -540,8 +541,8 @@
   /* check for possible mem corruption */
   if (outsize > GST_BUFFER_SIZE (outbuf)) {
     /* this is an error that when it happens, would need fixing in the
-     * resample library; we told
-     * it we wanted only GST_BUFFER_SIZE (outbuf), and it gave us more ! */
+     * resample library; we told it we wanted only GST_BUFFER_SIZE (outbuf),
+     * and it gave us more ! */
     GST_WARNING_OBJECT (audioresample,
         "audioresample, you memory corrupting bastard. "
         "you gave me outsize %d while my buffer was size %d",
@@ -556,9 +557,51 @@
   }
   GST_BUFFER_SIZE (outbuf) = outsize;
 
+  if (G_UNLIKELY (audioresample->need_discont)) {
+    GST_DEBUG_OBJECT (audioresample,
+        "marking this buffer with the DISCONT flag");
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+    audioresample->need_discont = FALSE;
+  }
+
+  GST_LOG_OBJECT (audioresample, "transformed to buffer of %ld bytes, ts %"
+      GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
+      G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
+      outsize, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
+      GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
+
+
   return GST_FLOW_OK;
 }
 
+/* llabs() is C99, so we might not have it; just use a simple macro... */
+#define LLABS(x) ((x>0)?x:-x)
+static gboolean
+audioresample_check_discont (GstAudioresample * audioresample,
+    GstClockTime timestamp)
+{
+  if (timestamp != GST_CLOCK_TIME_NONE &&
+      audioresample->prev_ts != GST_CLOCK_TIME_NONE &&
+      audioresample->prev_duration != GST_CLOCK_TIME_NONE &&
+      timestamp != audioresample->prev_ts + audioresample->prev_duration) {
+    /* Potentially a discontinuous buffer. However, it turns out that many
+     * elements generate imperfect streams due to rounding errors, so we permit
+     * a small error (up to one sample) without triggering a filter 
+     * flush/restart (if triggered incorrectly, this will be audible) */
+    GstClockTimeDiff diff = timestamp -
+        (audioresample->prev_ts + audioresample->prev_duration);
+
+    if (LLABS (diff) > GST_SECOND / audioresample->i_rate) {
+      GST_WARNING_OBJECT (audioresample,
+          "encountered timestamp discontinuity of %" G_GINT64_FORMAT, diff);
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
 static GstFlowReturn
 audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
     GstBuffer * outbuf)
@@ -576,7 +619,22 @@
   size = GST_BUFFER_SIZE (inbuf);
   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
 
-  GST_DEBUG_OBJECT (audioresample, "got buffer of %ld bytes", size);
+  GST_LOG_OBJECT (audioresample, "transforming buffer of %ld bytes, ts %"
+      GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
+      G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
+      size, GST_TIME_ARGS (timestamp),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
+      GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
+
+  /* check for timestamp discontinuities and flush/reset if needed */
+  if (G_UNLIKELY (audioresample_check_discont (audioresample, timestamp))) {
+    /* Flush internal samples */
+    audioresample_pushthrough (audioresample);
+    /* Inform downstream element about discontinuity */
+    audioresample->need_discont = TRUE;
+    /* We want to recalculate the offset */
+    audioresample->ts_offset = -1;
+  }
 
   if (audioresample->ts_offset == -1) {
     /* if we don't know the initial offset yet, calculate it based on the 
@@ -584,19 +642,21 @@
     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
       GstClockTime stime;
 
-      /* offset used to calculate the timestamps. We use the sample offset for this
-       * to make it more accurate. We want the first buffer to have the same timestamp
-       * as the incomming timestamp. */
+      /* offset used to calculate the timestamps. We use the sample offset for
+       * this to make it more accurate. We want the first buffer to have the
+       * same timestamp as the incoming timestamp. */
       audioresample->next_ts = timestamp;
       audioresample->ts_offset =
           gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND);
-      /* offset used to set as the buffer offset, this offset is always relative
-       * to the stream time, note that timestamp is not... */
+      /* offset used to set as the buffer offset, this offset is always
+       * relative to the stream time, note that timestamp is not... */
       stime = (timestamp - base->segment.start) + base->segment.time;
       audioresample->offset =
           gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND);
     }
   }
+  audioresample->prev_ts = timestamp;
+  audioresample->prev_duration = GST_BUFFER_DURATION (inbuf);
 
   /* need to memdup, resample takes ownership. */
   datacopy = g_memdup (data, size);
@@ -618,17 +678,25 @@
   r = audioresample->resample;
 
   outsize = resample_get_output_size (r);
-  if (outsize == 0)
+  if (outsize == 0) {
+    GST_DEBUG_OBJECT (audioresample, "no internal buffers needing flush");
     goto done;
+  }
+
+  trans = GST_BASE_TRANSFORM (audioresample);
 
-  outbuf = gst_buffer_new_and_alloc (outsize);
+  res = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET_NONE, outsize,
+      GST_PAD_CAPS (trans->srcpad), &outbuf);
+  if (G_UNLIKELY (res != GST_FLOW_OK)) {
+    GST_WARNING_OBJECT (audioresample, "failed allocating buffer of %d bytes",
+        outsize);
+    goto done;
+  }
 
   res = audioresample_do_output (audioresample, outbuf);
-  if (res != GST_FLOW_OK)
+  if (G_UNLIKELY (res != GST_FLOW_OK))
     goto done;
 
-  trans = GST_BASE_TRANSFORM (audioresample);
-
   res = gst_pad_push (trans->srcpad, outbuf);
 
 done:
--- gst/audioresample/gstaudioresample.h	2006/06/01 19:19:50	1.6
+++ gst/audioresample/gstaudioresample.h	2007/03/14 17:16:30	1.7
@@ -53,10 +53,12 @@
   GstCaps *srccaps, *sinkcaps;
 
   gboolean passthru;
+  gboolean need_discont;
 
   guint64 offset;
   guint64 ts_offset;
   GstClockTime next_ts;
+  GstClockTime prev_ts, prev_duration;
   int channels;
 
   int i_rate;