• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com>
3  *
4  * Authors: Peter van Hardenberg <pvh@songbirdnest.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /* Based on ADPCM encoders in libsndfile,
23    Copyright (C) 1999-2002 Erik de Castro Lopo <erikd@zip.com.au
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include <gst/gst.h>
31 #include <gst/audio/gstaudioencoder.h>
32 
33 #define GST_TYPE_ADPCM_ENC \
34     (adpcmenc_get_type ())
35 
36 #define GST_TYPE_ADPCMENC_LAYOUT \
37     (adpcmenc_layout_get_type ())
38 
39 #define GST_ADPCM_ENC(obj) \
40     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ADPCM_ENC, ADPCMEnc))
41 
42 #define GST_CAT_DEFAULT adpcmenc_debug
43 GST_DEBUG_CATEGORY_STATIC (adpcmenc_debug);
44 
45 static GstStaticPadTemplate adpcmenc_sink_template =
46 GST_STATIC_PAD_TEMPLATE ("sink",
47     GST_PAD_SINK,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("audio/x-raw, "
50         "format = (string) " GST_AUDIO_NE (S16) ", "
51         "layout = (string) interleaved, "
52         "rate = (int) [1, MAX], channels = (int) [1,2]")
53     );
54 
55 static GstStaticPadTemplate adpcmenc_src_template =
56     GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("audio/x-adpcm, "
60         " layout=(string)dvi, "
61         " block_align = (int) [64, 8192], "
62         " rate = (int)[ 1, MAX ], " "channels = (int)[1,2];")
63     );
64 
65 #define MIN_ADPCM_BLOCK_SIZE 64
66 #define MAX_ADPCM_BLOCK_SIZE 8192
67 #define DEFAULT_ADPCM_BLOCK_SIZE 1024
68 #define DEFAULT_ADPCM_LAYOUT LAYOUT_ADPCM_DVI
69 
70 static const int ima_indx_adjust[16] = {
71   -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8,
72 };
73 
74 static const int ima_step_size[89] = {
75   7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
76   50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
77   253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
78   1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
79   3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
80   11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
81   32767
82 };
83 
84 
85 enum adpcm_properties
86 {
87   PROP_0,
88   PROP_BLOCK_SIZE,
89   PROP_LAYOUT
90 };
91 
92 enum adpcm_layout
93 {
94   LAYOUT_ADPCM_DVI
95 };
96 
97 static GType
adpcmenc_layout_get_type(void)98 adpcmenc_layout_get_type (void)
99 {
100   static GType adpcmenc_layout_type = 0;
101 
102   if (!adpcmenc_layout_type) {
103     static const GEnumValue layout_types[] = {
104       {LAYOUT_ADPCM_DVI, "DVI/IMA APDCM", "dvi"},
105       {0, NULL, NULL},
106     };
107 
108     adpcmenc_layout_type = g_enum_register_static ("GstADPCMEncLayout",
109         layout_types);
110   }
111 
112   return adpcmenc_layout_type;
113 }
114 
115 typedef struct _ADPCMEncClass
116 {
117   GstAudioEncoderClass parent_class;
118 } ADPCMEncClass;
119 
120 typedef struct _ADPCMEnc
121 {
122   GstAudioEncoder parent;
123 
124   enum adpcm_layout layout;
125   int rate;
126   int channels;
127   int blocksize;
128   int samples_per_block;
129 
130   guint8 step_index[2];
131 
132 } ADPCMEnc;
133 
134 GType adpcmenc_get_type (void);
135 G_DEFINE_TYPE (ADPCMEnc, adpcmenc, GST_TYPE_AUDIO_ENCODER);
136 
137 static gboolean
adpcmenc_setup(ADPCMEnc * enc)138 adpcmenc_setup (ADPCMEnc * enc)
139 {
140   const int DVI_IMA_HEADER_SIZE = 4;
141   const int ADPCM_SAMPLES_PER_BYTE = 2;
142   guint64 sample_bytes;
143   const char *layout;
144   GstCaps *caps;
145   gboolean ret;
146 
147   switch (enc->layout) {
148     case LAYOUT_ADPCM_DVI:
149       layout = "dvi";
150       /* IMA ADPCM includes a 4-byte header per channel, */
151       sample_bytes = enc->blocksize - (DVI_IMA_HEADER_SIZE * enc->channels);
152       /* two samples per byte, plus a single sample in the header. */
153       enc->samples_per_block =
154           ((sample_bytes * ADPCM_SAMPLES_PER_BYTE) / enc->channels) + 1;
155       break;
156     default:
157       GST_WARNING_OBJECT (enc, "Invalid layout");
158       return FALSE;
159   }
160 
161   caps = gst_caps_new_simple ("audio/x-adpcm",
162       "rate", G_TYPE_INT, enc->rate,
163       "channels", G_TYPE_INT, enc->channels,
164       "layout", G_TYPE_STRING, layout,
165       "block_align", G_TYPE_INT, enc->blocksize, NULL);
166 
167   ret = gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (enc), caps);
168   gst_caps_unref (caps);
169 
170   /* Step index state is carried between blocks. */
171   enc->step_index[0] = 0;
172   enc->step_index[1] = 0;
173 
174   return ret;
175 }
176 
177 static gboolean
adpcmenc_set_format(GstAudioEncoder * benc,GstAudioInfo * info)178 adpcmenc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
179 {
180   ADPCMEnc *enc = (ADPCMEnc *) (benc);
181 
182   enc->rate = GST_AUDIO_INFO_RATE (info);
183   enc->channels = GST_AUDIO_INFO_CHANNELS (info);
184 
185   if (!adpcmenc_setup (enc))
186     return FALSE;
187 
188   /* report needs to base class */
189   gst_audio_encoder_set_frame_samples_min (benc, enc->samples_per_block);
190   gst_audio_encoder_set_frame_samples_max (benc, enc->samples_per_block);
191   gst_audio_encoder_set_frame_max (benc, 1);
192 
193   return TRUE;
194 }
195 
196 static void
adpcmenc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)197 adpcmenc_set_property (GObject * object,
198     guint prop_id, const GValue * value, GParamSpec * pspec)
199 {
200   ADPCMEnc *enc = GST_ADPCM_ENC (object);
201 
202   switch (prop_id) {
203     case PROP_BLOCK_SIZE:
204       enc->blocksize = g_value_get_int (value);
205       break;
206     case PROP_LAYOUT:
207       enc->layout = g_value_get_enum (value);
208       break;
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211       break;
212   }
213 }
214 
215 static void
adpcmenc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)216 adpcmenc_get_property (GObject * object,
217     guint prop_id, GValue * value, GParamSpec * pspec)
218 {
219   ADPCMEnc *enc = GST_ADPCM_ENC (object);
220 
221   switch (prop_id) {
222     case PROP_BLOCK_SIZE:
223       g_value_set_int (value, enc->blocksize);
224       break;
225     case PROP_LAYOUT:
226       g_value_set_enum (value, enc->layout);
227       break;
228     default:
229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230       break;
231   }
232 }
233 
234 static guint8
adpcmenc_encode_ima_sample(gint16 sample,gint16 * prev_sample,guint8 * stepindex)235 adpcmenc_encode_ima_sample (gint16 sample, gint16 * prev_sample,
236     guint8 * stepindex)
237 {
238   const int NEGATIVE_SIGN_BIT = 0x8;
239   int diff, vpdiff, mask, step;
240   int bytecode = 0x0;
241   diff = sample - *prev_sample;
242   step = ima_step_size[*stepindex];
243   vpdiff = step >> 3;
244 
245   if (diff < 0) {
246     diff = -diff;
247     bytecode = NEGATIVE_SIGN_BIT;
248   }
249 
250   mask = 0x4;
251   while (mask > 0) {
252     if (diff >= step) {
253       bytecode |= mask;
254       diff -= step;
255       vpdiff += step;
256     }
257     step >>= 1;
258     mask >>= 1;
259   }
260 
261   if (bytecode & 8) {
262     vpdiff = -vpdiff;
263   }
264 
265   *prev_sample = CLAMP (*prev_sample + vpdiff, G_MININT16, G_MAXINT16);
266   *stepindex = CLAMP (*stepindex + ima_indx_adjust[bytecode], 0, 88);
267 
268   return bytecode;
269 }
270 
271 static gboolean
adpcmenc_encode_ima_block(ADPCMEnc * enc,const gint16 * samples,guint8 * outbuf)272 adpcmenc_encode_ima_block (ADPCMEnc * enc, const gint16 * samples,
273     guint8 * outbuf)
274 {
275   const int HEADER_SIZE = 4;
276   gint16 prev_sample[2] = { 0, 0 };
277   guint32 write_pos = 0;
278   guint32 read_pos = 0;
279   guint8 channel = 0;
280 
281   /* Write a header for each channel.
282    * The header consists of a sixteen-bit predicted sound value,
283    * and an eight bit step_index, carried forward from any previous block.
284    * These allow seeking within the file.
285    */
286   for (channel = 0; channel < enc->channels; channel++) {
287     write_pos = channel * HEADER_SIZE;
288     outbuf[write_pos + 0] = (samples[channel] & 0xFF);
289     outbuf[write_pos + 1] = (samples[channel] >> 8) & 0xFF;
290     outbuf[write_pos + 2] = enc->step_index[channel];
291     outbuf[write_pos + 3] = 0;
292     prev_sample[channel] = samples[channel];
293   }
294 
295   /* raw-audio looks like this for a stereo stream:
296    * [ L, R, L, R, L, R ... ]
297    * encoded audio is in eight-sample blocks, two samples to a byte thusly:
298    * [ LL, LL, LL, LL, RR, RR, RR, RR ... ]
299    */
300   write_pos = HEADER_SIZE * enc->channels;
301   read_pos = enc->channels;     /* the first sample is in the header. */
302   while (write_pos < enc->blocksize) {
303     gint8 CHANNEL_CHUNK_SIZE = 8;
304     for (channel = 0; channel < enc->channels; channel++) {
305       /* convert eight samples (four bytes) per channel, then swap */
306       guint32 channel_chunk_base = read_pos + channel;
307       gint8 chunk;
308       for (chunk = 0; chunk < CHANNEL_CHUNK_SIZE; chunk++) {
309         guint8 packed_byte = 0, encoded_sample;
310         encoded_sample =
311             adpcmenc_encode_ima_sample (samples[channel_chunk_base +
312                 (chunk * enc->channels)], &prev_sample[channel],
313             &enc->step_index[channel]);
314         packed_byte |= encoded_sample & 0x0F;
315 
316         chunk++;
317 
318         encoded_sample =
319             adpcmenc_encode_ima_sample (samples[channel_chunk_base +
320                 (chunk * enc->channels)], &prev_sample[channel],
321             &enc->step_index[channel]);
322         packed_byte |= encoded_sample << 4 & 0xF0;
323 
324         outbuf[write_pos++] = packed_byte;
325       }
326     }
327     /* advance to the next block of 8 samples per channel */
328     read_pos += CHANNEL_CHUNK_SIZE * enc->channels;
329     if (read_pos > enc->samples_per_block * enc->channels) {
330       GST_LOG ("Ran past the end. (Reading %i of %i.)", read_pos,
331           enc->samples_per_block);
332     }
333   }
334 
335   return TRUE;
336 }
337 
338 static GstBuffer *
adpcmenc_encode_block(ADPCMEnc * enc,const gint16 * samples,int blocksize)339 adpcmenc_encode_block (ADPCMEnc * enc, const gint16 * samples, int blocksize)
340 {
341   gboolean res = FALSE;
342   GstBuffer *outbuf = NULL;
343   GstMapInfo omap;
344 
345   if (enc->layout == LAYOUT_ADPCM_DVI) {
346     outbuf = gst_buffer_new_and_alloc (enc->blocksize);
347     gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
348     res = adpcmenc_encode_ima_block (enc, samples, omap.data);
349     gst_buffer_unmap (outbuf, &omap);
350   } else {
351     /* should not happen afaics */
352     g_assert_not_reached ();
353     GST_WARNING_OBJECT (enc, "Unknown layout");
354     res = FALSE;
355   }
356 
357   if (!res) {
358     if (outbuf)
359       gst_buffer_unref (outbuf);
360     outbuf = NULL;
361     GST_WARNING_OBJECT (enc, "Encode of block failed");
362   }
363 
364   return outbuf;
365 }
366 
367 static GstFlowReturn
adpcmenc_handle_frame(GstAudioEncoder * benc,GstBuffer * buffer)368 adpcmenc_handle_frame (GstAudioEncoder * benc, GstBuffer * buffer)
369 {
370   ADPCMEnc *enc = (ADPCMEnc *) (benc);
371   GstFlowReturn ret = GST_FLOW_OK;
372   gint16 *samples;
373   GstBuffer *outbuf;
374   int input_bytes_per_block;
375   const int BYTES_PER_SAMPLE = 2;
376   GstMapInfo map;
377 
378   /* we don't deal with squeezing remnants, so simply discard those */
379   if (G_UNLIKELY (buffer == NULL)) {
380     GST_DEBUG_OBJECT (benc, "no data");
381     goto done;
382   }
383 
384   input_bytes_per_block =
385       enc->samples_per_block * BYTES_PER_SAMPLE * enc->channels;
386 
387   gst_buffer_map (buffer, &map, GST_MAP_READ);
388   if (G_UNLIKELY (map.size < input_bytes_per_block)) {
389     GST_DEBUG_OBJECT (enc, "discarding trailing data %d", (gint) map.size);
390     gst_buffer_unmap (buffer, &map);
391     ret = gst_audio_encoder_finish_frame (benc, NULL, -1);
392     goto done;
393   }
394 
395   samples = (gint16 *) map.data;
396   outbuf = adpcmenc_encode_block (enc, samples, enc->blocksize);
397   gst_buffer_unmap (buffer, &map);
398 
399   ret = gst_audio_encoder_finish_frame (benc, outbuf, enc->samples_per_block);
400 
401 done:
402   return ret;
403 }
404 
405 static gboolean
adpcmenc_start(GstAudioEncoder * enc)406 adpcmenc_start (GstAudioEncoder * enc)
407 {
408   GST_DEBUG_OBJECT (enc, "start");
409 
410   return TRUE;
411 }
412 
413 static gboolean
adpcmenc_stop(GstAudioEncoder * enc)414 adpcmenc_stop (GstAudioEncoder * enc)
415 {
416   GST_DEBUG_OBJECT (enc, "stop");
417 
418   return TRUE;
419 }
420 
421 static void
adpcmenc_init(ADPCMEnc * enc)422 adpcmenc_init (ADPCMEnc * enc)
423 {
424   GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (enc));
425 
426   /* Set defaults. */
427   enc->blocksize = DEFAULT_ADPCM_BLOCK_SIZE;
428   enc->layout = DEFAULT_ADPCM_LAYOUT;
429 }
430 
431 static void
adpcmenc_class_init(ADPCMEncClass * klass)432 adpcmenc_class_init (ADPCMEncClass * klass)
433 {
434   GObjectClass *gobjectclass = (GObjectClass *) klass;
435   GstElementClass *element_class = (GstElementClass *) klass;
436   GstAudioEncoderClass *base_class = (GstAudioEncoderClass *) klass;
437 
438   gobjectclass->set_property = adpcmenc_set_property;
439   gobjectclass->get_property = adpcmenc_get_property;
440 
441   gst_element_class_add_static_pad_template (element_class,
442       &adpcmenc_sink_template);
443   gst_element_class_add_static_pad_template (element_class,
444       &adpcmenc_src_template);
445   gst_element_class_set_static_metadata (element_class, "ADPCM encoder",
446       "Codec/Encoder/Audio", "Encode ADPCM audio",
447       "Pioneers of the Inevitable <songbird@songbirdnest.com>");
448 
449   base_class->start = GST_DEBUG_FUNCPTR (adpcmenc_start);
450   base_class->stop = GST_DEBUG_FUNCPTR (adpcmenc_stop);
451   base_class->set_format = GST_DEBUG_FUNCPTR (adpcmenc_set_format);
452   base_class->handle_frame = GST_DEBUG_FUNCPTR (adpcmenc_handle_frame);
453 
454   g_object_class_install_property (gobjectclass, PROP_LAYOUT,
455       g_param_spec_enum ("layout", "Layout",
456           "Layout for output stream",
457           GST_TYPE_ADPCMENC_LAYOUT, DEFAULT_ADPCM_LAYOUT,
458           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
459 
460   g_object_class_install_property (gobjectclass, PROP_BLOCK_SIZE,
461       g_param_spec_int ("blockalign", "Block Align",
462           "Block size for output stream",
463           MIN_ADPCM_BLOCK_SIZE, MAX_ADPCM_BLOCK_SIZE,
464           DEFAULT_ADPCM_BLOCK_SIZE,
465           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
466 }
467 
468 static gboolean
plugin_init(GstPlugin * plugin)469 plugin_init (GstPlugin * plugin)
470 {
471   GST_DEBUG_CATEGORY_INIT (adpcmenc_debug, "adpcmenc", 0, "ADPCM Encoders");
472   if (!gst_element_register (plugin, "adpcmenc", GST_RANK_PRIMARY,
473           GST_TYPE_ADPCM_ENC)) {
474     return FALSE;
475   }
476   return TRUE;
477 }
478 
479 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, adpcmenc,
480     "ADPCM encoder", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
481     GST_PACKAGE_ORIGIN);
482