Tizen 2.1 base
[profile/ivi/gst-plugins-ugly0.10.git] / gst / iec958 / ac3iec.c
1 /* GStreamer
2  * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
3                  2005 Michael Smith <msmith@fluendo.com>
4  *
5  * ac3iec.c: Pad AC3 frames into IEC958 frames for the S/PDIF interface.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <string.h>
28
29 #include <gst/gst.h>
30
31 #include "ac3iec.h"
32
33
34 GST_DEBUG_CATEGORY_STATIC (ac3iec_debug);
35 #define GST_CAT_DEFAULT (ac3iec_debug)
36
37
38 /* The duration of a single IEC958 frame. */
39 #define IEC958_FRAME_DURATION (32 * GST_MSECOND)
40
41 /* AC3IEC signals and args */
42 enum
43 {
44   LAST_SIGNAL,
45 };
46
47 enum
48 {
49   PROP_0,
50   PROP_RAW_AUDIO,
51 };
52
53
54 static GstStaticPadTemplate ac3iec_sink_template =
55     GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("audio/x-private1-ac3; audio/x-ac3; audio/ac3")
59     );
60
61 /* Two different output caps are possible. */
62 #define NORMAL_CAPS_DEF "audio/x-iec958, rate = (int){32000, 44100, 48000}"
63 #define RAW_AUDIO_CAPS_DEF "audio/x-raw-int, " \
64     "endianness = (int) " G_STRINGIFY (G_BIG_ENDIAN) ", " \
65     "signed = (boolean) true, " \
66     "width = (int) 16, " \
67     "depth = (int) 16, " \
68     "rate = (int){32000, 44100, 48000}, " \
69     "channels = (int) 2"
70
71 static GstStaticCaps normal_caps = GST_STATIC_CAPS (NORMAL_CAPS_DEF);
72 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS (RAW_AUDIO_CAPS_DEF);
73
74 static GstStaticPadTemplate ac3iec_src_template =
75     GST_STATIC_PAD_TEMPLATE ("src",
76     GST_PAD_SRC,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS (NORMAL_CAPS_DEF ";" RAW_AUDIO_CAPS_DEF));
79
80 static void ac3iec_base_init (gpointer g_class);
81 static void ac3iec_class_init (AC3IECClass * klass);
82 static void ac3iec_init (AC3IEC * ac3iec);
83 static void ac3iec_finalize (GObject * object);
84
85 static void ac3iec_set_property (GObject * object,
86     guint prop_id, const GValue * value, GParamSpec * pspec);
87 static void ac3iec_get_property (GObject * object,
88     guint prop_id, GValue * value, GParamSpec * pspec);
89
90 static gboolean ac3iec_setcaps (GstPad * pad, GstCaps * caps);
91 static GstFlowReturn ac3iec_chain_dvd (GstPad * pad, GstBuffer * buf);
92 static GstFlowReturn ac3iec_chain_raw (GstPad * pad, GstBuffer * buf);
93
94 static GstStateChangeReturn ac3iec_change_state (GstElement * element,
95     GstStateChange transition);
96
97
98 static GstElementClass *parent_class = NULL;
99
100 /* static guint ac3iec_signals[LAST_SIGNAL] = { 0 }; */
101
102
103 GType
104 ac3iec_get_type (void)
105 {
106   static GType ac3iec_type = 0;
107
108   if (!ac3iec_type) {
109     static const GTypeInfo ac3iec_info = {
110       sizeof (AC3IECClass),
111       ac3iec_base_init,
112       NULL,
113       (GClassInitFunc) ac3iec_class_init,
114       NULL,
115       NULL,
116       sizeof (AC3IEC),
117       0,
118       (GInstanceInitFunc) ac3iec_init,
119     };
120     ac3iec_type = g_type_register_static (GST_TYPE_ELEMENT,
121         "AC3IEC", &ac3iec_info, 0);
122
123     GST_DEBUG_CATEGORY_INIT (ac3iec_debug, "ac3iec", 0,
124         "AC3 to IEC958 padding element");
125   }
126   return ac3iec_type;
127 }
128
129 static void
130 ac3iec_base_init (gpointer g_class)
131 {
132   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
133
134   gst_element_class_set_details_simple (element_class, "AC3 to IEC958 filter",
135       "Codec/Muxer/Audio",
136       "Pads AC3 frames into IEC958 frames suitable for a raw S/PDIF interface",
137       "Martin Soto <martinsoto@users.sourceforge.net>");
138   gst_element_class_add_static_pad_template (element_class,
139       &ac3iec_sink_template);
140   gst_element_class_add_static_pad_template (element_class,
141       &ac3iec_src_template);
142 }
143
144
145 static void
146 ac3iec_class_init (AC3IECClass * klass)
147 {
148   GObjectClass *gobject_class;
149   GstElementClass *gstelement_class;
150
151   gobject_class = (GObjectClass *) klass;
152   gstelement_class = (GstElementClass *) klass;
153
154   parent_class = g_type_class_peek_parent (klass);
155
156   gobject_class->set_property = ac3iec_set_property;
157   gobject_class->get_property = ac3iec_get_property;
158   gobject_class->finalize = ac3iec_finalize;
159
160   g_object_class_install_property (gobject_class, PROP_RAW_AUDIO,
161       g_param_spec_boolean ("raw-audio", "raw-audio",
162           "If true, source pad caps are set to raw audio.",
163           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
164
165   gstelement_class->change_state = ac3iec_change_state;
166 }
167
168
169 static void
170 ac3iec_init (AC3IEC * ac3iec)
171 {
172   ac3iec->sink =
173       gst_pad_new_from_static_template (&ac3iec_sink_template, "sink");
174   gst_pad_set_setcaps_function (ac3iec->sink, ac3iec_setcaps);
175   gst_pad_set_chain_function (ac3iec->sink, ac3iec_chain_dvd);
176   gst_element_add_pad (GST_ELEMENT (ac3iec), ac3iec->sink);
177
178   ac3iec->src = gst_pad_new_from_static_template (&ac3iec_src_template, "src");
179   gst_pad_use_fixed_caps (ac3iec->src);
180   gst_element_add_pad (GST_ELEMENT (ac3iec), ac3iec->src);
181
182   ac3iec->cur_ts = GST_CLOCK_TIME_NONE;
183
184   ac3iec->padder = g_malloc (sizeof (ac3_padder));
185 }
186
187
188 static void
189 ac3iec_finalize (GObject * object)
190 {
191   AC3IEC *ac3iec = AC3IEC (object);
192
193   g_free (ac3iec->padder);
194
195   G_OBJECT_CLASS (parent_class)->finalize (object);
196 }
197
198 static void
199 ac3iec_set_property (GObject * object, guint prop_id,
200     const GValue * value, GParamSpec * pspec)
201 {
202   AC3IEC *ac3iec = AC3IEC (object);
203
204   switch (prop_id) {
205     case PROP_RAW_AUDIO:
206     {
207       ac3iec->raw_audio = g_value_get_boolean (value);
208       break;
209     }
210     default:
211       break;
212   }
213 }
214
215 static gboolean
216 ac3iec_setcaps (GstPad * pad, GstCaps * caps)
217 {
218   AC3IEC *ac3iec = AC3IEC (gst_pad_get_parent (pad));
219   GstStructure *structure = gst_caps_get_structure (caps, 0);
220
221   if (structure && gst_structure_has_name (structure, "audio/x-private1-ac3"))
222     ac3iec->dvdmode = TRUE;
223   else
224     ac3iec->dvdmode = FALSE;
225
226   gst_object_unref (ac3iec);
227
228   return TRUE;
229 }
230
231 static void
232 ac3iec_get_property (GObject * object, guint prop_id,
233     GValue * value, GParamSpec * pspec)
234 {
235   AC3IEC *ac3iec = AC3IEC (object);
236
237   switch (prop_id) {
238     case PROP_RAW_AUDIO:
239       g_value_set_boolean (value, ac3iec->raw_audio);
240       break;
241     default:
242       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243       break;
244   }
245 }
246
247 static GstFlowReturn
248 ac3iec_chain_dvd (GstPad * pad, GstBuffer * buf)
249 {
250   guint first_access;
251   guint8 *data;
252   guint size;
253   gint offset;
254   gint len;
255   GstBuffer *subbuf;
256   GstFlowReturn ret;
257   AC3IEC *ac3iec = AC3IEC (gst_pad_get_parent (pad));
258
259   if (ac3iec->dvdmode) {
260     size = GST_BUFFER_SIZE (buf);
261     data = GST_BUFFER_DATA (buf);
262
263     first_access = (data[0] << 8) | data[1];
264
265     /* Skip the first_access header */
266     offset = 2;
267
268     if (first_access > 1) {
269       /* Length of data before first_access */
270       len = first_access - 1;
271
272       /* Ensure we don't crash if fed totally invalid data */
273       if (offset + len > size) {
274         ret = GST_FLOW_ERROR;
275         goto done;
276       }
277
278       if (len > 0) {
279         subbuf = gst_buffer_create_sub (buf, offset, len);
280         GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
281         ret = ac3iec_chain_raw (pad, subbuf);
282         if (ret != GST_FLOW_OK)
283           goto done;
284       }
285
286       offset += len;
287       len = size - offset;
288
289       subbuf = gst_buffer_create_sub (buf, offset, len);
290       GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
291
292       ret = ac3iec_chain_raw (pad, subbuf);
293     } else {
294       /* Ensure we don't crash if fed totally invalid data */
295       if (size < 2) {
296         ret = GST_FLOW_ERROR;
297         goto done;
298       }
299
300       /* No first_access, so no timestamp */
301       subbuf = gst_buffer_create_sub (buf, offset, size - offset);
302       GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
303       ret = ac3iec_chain_raw (pad, subbuf);
304     }
305   } else {
306     ret = ac3iec_chain_raw (pad, buf);
307     gst_object_unref (ac3iec);
308     return ret;
309   }
310
311 done:
312   gst_object_unref (ac3iec);
313   gst_buffer_unref (buf);
314
315   return ret;
316 }
317
318 static GstFlowReturn
319 ac3iec_chain_raw (GstPad * pad, GstBuffer * buf)
320 {
321   GstBuffer *new;
322   AC3IEC *ac3iec;
323   int event;
324   GstFlowReturn ret = GST_FLOW_OK;
325
326   g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
327   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
328   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
329
330   ac3iec = AC3IEC (gst_pad_get_parent (pad));
331
332   if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) {
333     /* Whoever tells me why it is necessary to add a frame in order to
334        get synchronized sound will get a beer from me. */
335     ac3iec->cur_ts = GST_BUFFER_TIMESTAMP (buf) + IEC958_FRAME_DURATION;
336   }
337
338   /* Push the new data into the padder. */
339   ac3p_push_data (ac3iec->padder, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
340
341   /* Parse the data. */
342   event = ac3p_parse (ac3iec->padder);
343   while (event != AC3P_EVENT_PUSH) {
344     if (event == AC3P_EVENT_FRAME) {
345       GstCaps *bufcaps = NULL;
346
347       if (ac3iec->caps == NULL) {
348         gint rate = ac3iec->padder->rate;
349
350         if (ac3iec->raw_audio) {
351           ac3iec->caps =
352               gst_caps_make_writable (gst_static_caps_get (&raw_audio_caps));
353         } else {
354           ac3iec->caps =
355               gst_caps_make_writable (gst_static_caps_get (&normal_caps));
356         }
357         gst_structure_set (gst_caps_get_structure (ac3iec->caps, 0), "rate",
358             G_TYPE_INT, rate, NULL);
359         gst_pad_set_caps (ac3iec->src, ac3iec->caps);
360       }
361
362       /* We have a new frame: */
363       bufcaps = GST_PAD_CAPS (ac3iec->src);
364
365       /* Create a new buffer, and copy the frame data into it. */
366       ret =
367           gst_pad_alloc_buffer_and_set_caps (ac3iec->src, 0,
368           AC3P_IEC_FRAME_SIZE, bufcaps, &new);
369       if (ret != GST_FLOW_OK)
370         goto buffer_alloc_failed;
371
372       memcpy (GST_BUFFER_DATA (new), ac3p_frame (ac3iec->padder),
373           AC3P_IEC_FRAME_SIZE);
374
375       /* Set the timestamp. */
376       GST_BUFFER_TIMESTAMP (new) = ac3iec->cur_ts;
377       GST_BUFFER_DURATION (new) = IEC958_FRAME_DURATION;
378       ac3iec->cur_ts = GST_CLOCK_TIME_NONE;
379
380       GST_LOG_OBJECT (ac3iec, "Pushing IEC958 buffer of size %d",
381           GST_BUFFER_SIZE (new));
382       /* Push the buffer to the source pad. */
383       ret = gst_pad_push (ac3iec->src, new);
384     }
385
386     event = ac3p_parse (ac3iec->padder);
387   }
388
389   gst_buffer_unref (buf);
390
391 done:
392   gst_object_unref (ac3iec);
393
394   return ret;
395
396 buffer_alloc_failed:
397   gst_buffer_unref (buf);
398   goto done;
399
400 }
401
402
403 static GstStateChangeReturn
404 ac3iec_change_state (GstElement * element, GstStateChange transition)
405 {
406   AC3IEC *ac3iec;
407   GstStateChangeReturn ret;
408
409   g_return_val_if_fail (GST_IS_AC3IEC (element), GST_STATE_CHANGE_FAILURE);
410
411   ac3iec = AC3IEC (element);
412
413   switch (transition) {
414     case GST_STATE_CHANGE_NULL_TO_READY:
415       break;
416     case GST_STATE_CHANGE_READY_TO_PAUSED:
417       ac3p_init (ac3iec->padder);
418       break;
419     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
420       break;
421     default:
422       break;
423   }
424
425   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
426
427   switch (transition) {
428     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
429       break;
430     case GST_STATE_CHANGE_PAUSED_TO_READY:
431       ac3p_clear (ac3iec->padder);
432       if (ac3iec->caps) {
433         gst_caps_unref (ac3iec->caps);
434         ac3iec->caps = NULL;
435       }
436       break;
437     case GST_STATE_CHANGE_READY_TO_NULL:
438       break;
439     default:
440       break;
441   }
442
443   return ret;
444 }
445
446
447 static gboolean
448 plugin_init (GstPlugin * plugin)
449 {
450   if (!gst_element_register (plugin, "ac3iec958", GST_RANK_NONE,
451           GST_TYPE_AC3IEC)) {
452     return FALSE;
453   }
454
455   return TRUE;
456 }
457
458
459 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
460     GST_VERSION_MINOR,
461     "iec958",
462     "Convert raw AC3 into IEC958 (S/PDIF) frames",
463     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);