Branch data Line data Source code
1 : : /* GStreamer
2 : : * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 : : * Copyright (C) <2004> Wim Taymans <wim@fluendo.com>
4 : : * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
5 : : * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
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 : : /**
24 : : * SECTION:element-lamemp3enc
25 : : * @see_also: lame, mad, vorbisenc
26 : : *
27 : : * This element encodes raw integer audio into an MPEG-1 layer 3 (MP3) stream.
28 : : * Note that <ulink url="http://en.wikipedia.org/wiki/MP3">MP3</ulink> is not
29 : : * a free format, there are licensing and patent issues to take into
30 : : * consideration. See <ulink url="http://www.vorbis.com/">Ogg/Vorbis</ulink>
31 : : * for a royalty free (and often higher quality) alternative.
32 : : *
33 : : * <refsect2>
34 : : * <title>Output sample rate</title>
35 : : * If no fixed output sample rate is negotiated on the element's src pad,
36 : : * the element will choose an optimal sample rate to resample to internally.
37 : : * For example, a 16-bit 44.1 KHz mono audio stream encoded at 48 kbit will
38 : : * get resampled to 32 KHz. Use filter caps on the src pad to force a
39 : : * particular sample rate.
40 : : * </refsect2>
41 : : * <refsect2>
42 : : * <title>Example pipelines</title>
43 : : * |[
44 : : * gst-launch -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! lamemp3enc ! filesink location=sine.mp3
45 : : * ]| Encode a test sine signal to MP3.
46 : : * |[
47 : : * gst-launch -v alsasrc ! audioconvert ! lamemp3enc target=bitrate bitrate=192 ! filesink location=alsasrc.mp3
48 : : * ]| Record from a sound card using ALSA and encode to MP3 with an average bitrate of 192kbps
49 : : * |[
50 : : * gst-launch -v filesrc location=music.wav ! decodebin ! audioconvert ! audioresample ! lamemp3enc target=quality quality=0 ! id3v2mux ! filesink location=music.mp3
51 : : * ]| Transcode from a .wav file to MP3 (the id3v2mux element is optional) with best VBR quality
52 : : * |[
53 : : * gst-launch -v cdda://5 ! audioconvert ! lamemp3enc target=bitrate cbr=true bitrate=192 ! filesink location=track5.mp3
54 : : * ]| Encode Audio CD track 5 to MP3 with a constant bitrate of 192kbps
55 : : * |[
56 : : * gst-launch -v audiotestsrc num-buffers=10 ! audio/x-raw-int,rate=44100,channels=1 ! lamemp3enc target=bitrate cbr=true bitrate=48 ! filesink location=test.mp3
57 : : * ]| Encode to a fixed sample rate
58 : : * </refsect2>
59 : : *
60 : : * Since: 0.10.12
61 : : */
62 : :
63 : : #ifdef HAVE_CONFIG_H
64 : : #include "config.h"
65 : : #endif
66 : :
67 : : #include <string.h>
68 : : #include "gstlamemp3enc.h"
69 : : #include <gst/gst-i18n-plugin.h>
70 : :
71 : : /* lame < 3.98 */
72 : : #ifndef HAVE_LAME_SET_VBR_QUALITY
73 : : #define lame_set_VBR_quality(flags,q) lame_set_VBR_q((flags),(int)(q))
74 : : #endif
75 : :
76 : : GST_DEBUG_CATEGORY_STATIC (debug);
77 : : #define GST_CAT_DEFAULT debug
78 : :
79 : : /* elementfactory information */
80 : :
81 : : /* LAMEMP3ENC can do MPEG-1, MPEG-2, and MPEG-2.5, so it has 9 possible
82 : : * sample rates it supports */
83 : : static GstStaticPadTemplate gst_lamemp3enc_sink_template =
84 : : GST_STATIC_PAD_TEMPLATE ("sink",
85 : : GST_PAD_SINK,
86 : : GST_PAD_ALWAYS,
87 : : GST_STATIC_CAPS ("audio/x-raw-int, "
88 : : "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
89 : : "signed = (boolean) true, "
90 : : "width = (int) 16, "
91 : : "depth = (int) 16, "
92 : : "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
93 : : "channels = (int) [ 1, 2 ]")
94 : : );
95 : :
96 : : static GstStaticPadTemplate gst_lamemp3enc_src_template =
97 : : GST_STATIC_PAD_TEMPLATE ("src",
98 : : GST_PAD_SRC,
99 : : GST_PAD_ALWAYS,
100 : : GST_STATIC_CAPS ("audio/mpeg, "
101 : : "mpegversion = (int) 1, "
102 : : "layer = (int) 3, "
103 : : "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
104 : : "channels = (int) [ 1, 2 ]")
105 : : );
106 : :
107 : : /********** Define useful types for non-programmatic interfaces **********/
108 : : enum
109 : : {
110 : : LAMEMP3ENC_TARGET_QUALITY = 0,
111 : : LAMEMP3ENC_TARGET_BITRATE
112 : : };
113 : :
114 : : #define GST_TYPE_LAMEMP3ENC_TARGET (gst_lamemp3enc_target_get_type())
115 : : static GType
116 : 6 : gst_lamemp3enc_target_get_type (void)
117 : : {
118 : : static GType lame_target_type = 0;
119 : : static GEnumValue lame_targets[] = {
120 : : {LAMEMP3ENC_TARGET_QUALITY, "Quality", "quality"},
121 : : {LAMEMP3ENC_TARGET_BITRATE, "Bitrate", "bitrate"},
122 : : {0, NULL, NULL}
123 : : };
124 : :
125 [ + - ]: 6 : if (!lame_target_type) {
126 : 6 : lame_target_type =
127 : 6 : g_enum_register_static ("GstLameMP3EncTarget", lame_targets);
128 : : }
129 : 6 : return lame_target_type;
130 : : }
131 : :
132 : : enum
133 : : {
134 : : LAMEMP3ENC_ENCODING_ENGINE_QUALITY_FAST = 0,
135 : : LAMEMP3ENC_ENCODING_ENGINE_QUALITY_STANDARD,
136 : : LAMEMP3ENC_ENCODING_ENGINE_QUALITY_HIGH
137 : : };
138 : :
139 : : #define GST_TYPE_LAMEMP3ENC_ENCODING_ENGINE_QUALITY (gst_lamemp3enc_encoding_engine_quality_get_type())
140 : : static GType
141 : 6 : gst_lamemp3enc_encoding_engine_quality_get_type (void)
142 : : {
143 : : static GType lame_encoding_engine_quality_type = 0;
144 : : static GEnumValue lame_encoding_engine_quality[] = {
145 : : {0, "Fast", "fast"},
146 : : {1, "Standard", "standard"},
147 : : {2, "High", "high"},
148 : : {0, NULL, NULL}
149 : : };
150 : :
151 [ + - ]: 6 : if (!lame_encoding_engine_quality_type) {
152 : 6 : lame_encoding_engine_quality_type =
153 : 6 : g_enum_register_static ("GstLameMP3EncEncodingEngineQuality",
154 : : lame_encoding_engine_quality);
155 : : }
156 : 6 : return lame_encoding_engine_quality_type;
157 : : }
158 : :
159 : : /********** Standard stuff for signals and arguments **********/
160 : :
161 : : enum
162 : : {
163 : : ARG_0,
164 : : ARG_TARGET,
165 : : ARG_BITRATE,
166 : : ARG_CBR,
167 : : ARG_QUALITY,
168 : : ARG_ENCODING_ENGINE_QUALITY,
169 : : ARG_MONO
170 : : };
171 : :
172 : : #define DEFAULT_TARGET LAMEMP3ENC_TARGET_QUALITY
173 : : #define DEFAULT_BITRATE 128
174 : : #define DEFAULT_CBR FALSE
175 : : #define DEFAULT_QUALITY 4
176 : : #define DEFAULT_ENCODING_ENGINE_QUALITY LAMEMP3ENC_ENCODING_ENGINE_QUALITY_STANDARD
177 : : #define DEFAULT_MONO FALSE
178 : :
179 : : static void gst_lamemp3enc_base_init (gpointer g_class);
180 : : static void gst_lamemp3enc_class_init (GstLameMP3EncClass * klass);
181 : : static void gst_lamemp3enc_init (GstLameMP3Enc * gst_lame);
182 : :
183 : : static void gst_lamemp3enc_set_property (GObject * object, guint prop_id,
184 : : const GValue * value, GParamSpec * pspec);
185 : : static void gst_lamemp3enc_get_property (GObject * object, guint prop_id,
186 : : GValue * value, GParamSpec * pspec);
187 : : static gboolean gst_lamemp3enc_sink_event (GstPad * pad, GstEvent * event);
188 : : static GstFlowReturn gst_lamemp3enc_chain (GstPad * pad, GstBuffer * buf);
189 : : static gboolean gst_lamemp3enc_setup (GstLameMP3Enc * lame);
190 : : static GstStateChangeReturn gst_lamemp3enc_change_state (GstElement * element,
191 : : GstStateChange transition);
192 : :
193 : : static GstElementClass *parent_class = NULL;
194 : :
195 : : GType
196 : 69 : gst_lamemp3enc_get_type (void)
197 : : {
198 : : static GType gst_lamemp3enc_type = 0;
199 : :
200 [ + + ]: 69 : if (!gst_lamemp3enc_type) {
201 : : static const GTypeInfo gst_lamemp3enc_info = {
202 : : sizeof (GstLameMP3EncClass),
203 : : gst_lamemp3enc_base_init,
204 : : NULL,
205 : : (GClassInitFunc) gst_lamemp3enc_class_init,
206 : : NULL,
207 : : NULL,
208 : : sizeof (GstLameMP3Enc),
209 : : 0,
210 : : (GInstanceInitFunc) gst_lamemp3enc_init,
211 : : };
212 : : static const GInterfaceInfo preset_info = {
213 : : NULL,
214 : : NULL,
215 : : NULL
216 : : };
217 : :
218 : 7 : gst_lamemp3enc_type =
219 : 7 : g_type_register_static (GST_TYPE_ELEMENT, "GstLameMP3Enc",
220 : : &gst_lamemp3enc_info, 0);
221 : 7 : g_type_add_interface_static (gst_lamemp3enc_type, GST_TYPE_PRESET,
222 : : &preset_info);
223 : : }
224 : 69 : return gst_lamemp3enc_type;
225 : : }
226 : :
227 : : static void
228 : 9 : gst_lamemp3enc_release_memory (GstLameMP3Enc * lame)
229 : : {
230 [ + + ]: 9 : if (lame->lgf) {
231 : 1 : lame_close (lame->lgf);
232 : 1 : lame->lgf = NULL;
233 : : }
234 : 9 : }
235 : :
236 : : static void
237 : 4 : gst_lamemp3enc_finalize (GObject * obj)
238 : : {
239 : 4 : gst_lamemp3enc_release_memory (GST_LAMEMP3ENC (obj));
240 : :
241 : 4 : G_OBJECT_CLASS (parent_class)->finalize (obj);
242 : 4 : }
243 : :
244 : : static void
245 : 6 : gst_lamemp3enc_base_init (gpointer g_class)
246 : : {
247 : 6 : GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
248 : :
249 : 6 : gst_element_class_add_pad_template (element_class,
250 : : gst_static_pad_template_get (&gst_lamemp3enc_src_template));
251 : 6 : gst_element_class_add_pad_template (element_class,
252 : : gst_static_pad_template_get (&gst_lamemp3enc_sink_template));
253 : 6 : gst_element_class_set_details_simple (element_class, "L.A.M.E. mp3 encoder",
254 : : "Codec/Encoder/Audio",
255 : : "High-quality free MP3 encoder",
256 : : "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
257 : 6 : }
258 : :
259 : : static void
260 : 6 : gst_lamemp3enc_class_init (GstLameMP3EncClass * klass)
261 : : {
262 : : GObjectClass *gobject_class;
263 : : GstElementClass *gstelement_class;
264 : :
265 : 6 : gobject_class = (GObjectClass *) klass;
266 : 6 : gstelement_class = (GstElementClass *) klass;
267 : :
268 : 6 : parent_class = g_type_class_peek_parent (klass);
269 : :
270 : 6 : gobject_class->set_property = gst_lamemp3enc_set_property;
271 : 6 : gobject_class->get_property = gst_lamemp3enc_get_property;
272 : 6 : gobject_class->finalize = gst_lamemp3enc_finalize;
273 : :
274 : 6 : g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET,
275 : : g_param_spec_enum ("target", "Target",
276 : : "Optimize for quality or bitrate", GST_TYPE_LAMEMP3ENC_TARGET,
277 : : DEFAULT_TARGET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278 : 6 : g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
279 : : g_param_spec_int ("bitrate", "Bitrate (kb/s)",
280 : : "Bitrate in kbit/sec (Only valid if target is bitrate, for CBR one "
281 : : "of 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, "
282 : : "256 or 320)", 8, 320, DEFAULT_BITRATE,
283 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
284 : 6 : g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CBR,
285 : : g_param_spec_boolean ("cbr", "CBR", "Enforce constant bitrate encoding "
286 : : "(Only valid if target is bitrate)", DEFAULT_CBR,
287 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288 : 6 : g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
289 : : g_param_spec_float ("quality", "Quality",
290 : : "VBR Quality from 0 to 10, 0 being the best "
291 : : "(Only valid if target is quality)", 0.0, 9.999,
292 : : DEFAULT_QUALITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 : 6 : g_object_class_install_property (G_OBJECT_CLASS (klass),
294 : : ARG_ENCODING_ENGINE_QUALITY, g_param_spec_enum ("encoding-engine-quality",
295 : : "Encoding Engine Quality", "Quality/speed of the encoding engine, "
296 : : "this does not affect the bitrate!",
297 : : GST_TYPE_LAMEMP3ENC_ENCODING_ENGINE_QUALITY,
298 : : DEFAULT_ENCODING_ENGINE_QUALITY,
299 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
300 : 6 : g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MONO,
301 : : g_param_spec_boolean ("mono", "Mono", "Enforce mono encoding",
302 : : DEFAULT_MONO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
303 : :
304 : 6 : gstelement_class->change_state =
305 : 6 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_change_state);
306 : 6 : }
307 : :
308 : : static gboolean
309 : 1 : gst_lamemp3enc_src_setcaps (GstPad * pad, GstCaps * caps)
310 : : {
311 [ - + ]: 1 : GST_DEBUG_OBJECT (pad, "caps: %" GST_PTR_FORMAT, caps);
312 : 1 : return TRUE;
313 : : }
314 : :
315 : : static gboolean
316 : 1 : gst_lamemp3enc_sink_setcaps (GstPad * pad, GstCaps * caps)
317 : : {
318 : : GstLameMP3Enc *lame;
319 : : gint out_samplerate;
320 : : gint version;
321 : : GstStructure *structure;
322 : : GstCaps *othercaps;
323 : :
324 : 1 : lame = GST_LAMEMP3ENC (GST_PAD_PARENT (pad));
325 : 1 : structure = gst_caps_get_structure (caps, 0);
326 : :
327 [ - + ]: 1 : if (!gst_structure_get_int (structure, "rate", &lame->samplerate))
328 : 0 : goto no_rate;
329 [ - + ]: 1 : if (!gst_structure_get_int (structure, "channels", &lame->num_channels))
330 : 0 : goto no_channels;
331 : :
332 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "setting up lame");
333 [ - + ]: 1 : if (!gst_lamemp3enc_setup (lame))
334 : 0 : goto setup_failed;
335 : :
336 : :
337 : 1 : out_samplerate = lame_get_out_samplerate (lame->lgf);
338 [ - + ]: 1 : if (out_samplerate == 0)
339 : 0 : goto zero_output_rate;
340 [ - + ]: 1 : if (out_samplerate != lame->samplerate) {
341 [ # # ]: 0 : GST_WARNING_OBJECT (lame,
342 : : "output samplerate %d is different from incoming samplerate %d",
343 : : out_samplerate, lame->samplerate);
344 : : }
345 : :
346 : 1 : version = lame_get_version (lame->lgf);
347 [ - + ]: 1 : if (version == 0)
348 : 0 : version = 2;
349 [ + - ]: 1 : else if (version == 1)
350 : 1 : version = 1;
351 [ # # ]: 0 : else if (version == 2)
352 : 0 : version = 3;
353 : :
354 : 1 : othercaps =
355 [ + - ]: 1 : gst_caps_new_simple ("audio/mpeg",
356 : : "mpegversion", G_TYPE_INT, 1,
357 : : "mpegaudioversion", G_TYPE_INT, version,
358 : : "layer", G_TYPE_INT, 3,
359 : 1 : "channels", G_TYPE_INT, lame->mono ? 1 : lame->num_channels,
360 : : "rate", G_TYPE_INT, out_samplerate, NULL);
361 : :
362 : : /* and use these caps */
363 : 1 : gst_pad_set_caps (lame->srcpad, othercaps);
364 : 1 : gst_caps_unref (othercaps);
365 : :
366 : 1 : return TRUE;
367 : :
368 : : no_rate:
369 : : {
370 [ # # ]: 0 : GST_ERROR_OBJECT (lame, "input caps have no sample rate field");
371 : 0 : return FALSE;
372 : : }
373 : : no_channels:
374 : : {
375 [ # # ]: 0 : GST_ERROR_OBJECT (lame, "input caps have no channels field");
376 : 0 : return FALSE;
377 : : }
378 : : zero_output_rate:
379 : : {
380 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (lame, LIBRARY, SETTINGS, (NULL),
[ # # ][ # # ]
381 : : ("LAMEMP3ENC decided on a zero sample rate"));
382 : 0 : return FALSE;
383 : : }
384 : : setup_failed:
385 : : {
386 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (lame, LIBRARY, SETTINGS,
[ # # ][ # # ]
387 : : (_("Failed to configure LAMEMP3ENC encoder. Check your encoding parameters.")), (NULL));
388 : 1 : return FALSE;
389 : : }
390 : : }
391 : :
392 : : static GstCaps *
393 : 11 : gst_lamemp3enc_sink_getcaps (GstPad * pad)
394 : : {
395 : : const GstCaps *templ_caps;
396 : : GstLameMP3Enc *lame;
397 : 11 : GstCaps *allowed = NULL;
398 : : GstCaps *caps, *filter_caps;
399 : : gint i, j;
400 : :
401 : 11 : lame = GST_LAMEMP3ENC (gst_pad_get_parent (pad));
402 : :
403 : : /* we want to be able to communicate to upstream elements like audioconvert
404 : : * and audioresample any rate/channel restrictions downstream (e.g. muxer
405 : : * only accepting certain sample rates) */
406 : 11 : templ_caps = gst_pad_get_pad_template_caps (pad);
407 : 11 : allowed = gst_pad_get_allowed_caps (lame->srcpad);
408 [ + + ][ + - ]: 11 : if (!allowed || gst_caps_is_empty (allowed) || gst_caps_is_any (allowed)) {
[ - + ]
409 : 2 : caps = gst_caps_copy (templ_caps);
410 : 2 : goto done;
411 : : }
412 : :
413 : 9 : filter_caps = gst_caps_new_empty ();
414 : :
415 [ + + ]: 18 : for (i = 0; i < gst_caps_get_size (templ_caps); i++) {
416 : : GQuark q_name;
417 : :
418 : 9 : q_name = gst_structure_get_name_id (gst_caps_get_structure (templ_caps, i));
419 : :
420 : : /* pick rate + channel fields from allowed caps */
421 [ + + ]: 18 : for (j = 0; j < gst_caps_get_size (allowed); j++) {
422 : 9 : const GstStructure *allowed_s = gst_caps_get_structure (allowed, j);
423 : : const GValue *val;
424 : : GstStructure *s;
425 : :
426 : 9 : s = gst_structure_id_empty_new (q_name);
427 [ + - ]: 9 : if ((val = gst_structure_get_value (allowed_s, "rate")))
428 : 9 : gst_structure_set_value (s, "rate", val);
429 [ + - ]: 9 : if ((val = gst_structure_get_value (allowed_s, "channels")))
430 : 9 : gst_structure_set_value (s, "channels", val);
431 : :
432 : 9 : gst_caps_merge_structure (filter_caps, s);
433 : : }
434 : : }
435 : :
436 : 9 : caps = gst_caps_intersect (filter_caps, templ_caps);
437 : 9 : gst_caps_unref (filter_caps);
438 : :
439 : : done:
440 : :
441 : 11 : gst_caps_replace (&allowed, NULL);
442 : 11 : gst_object_unref (lame);
443 : :
444 : 11 : return caps;
445 : : }
446 : :
447 : : static gint64
448 : 1 : gst_lamemp3enc_get_latency (GstLameMP3Enc * lame)
449 : : {
450 : 1 : return gst_util_uint64_scale_int (lame_get_framesize (lame->lgf),
451 : : GST_SECOND, lame->samplerate);
452 : : }
453 : :
454 : : static gboolean
455 : 1 : gst_lamemp3enc_src_query (GstPad * pad, GstQuery * query)
456 : : {
457 : 1 : gboolean res = TRUE;
458 : : GstLameMP3Enc *lame;
459 : : GstPad *peerpad;
460 : :
461 : 1 : lame = GST_LAMEMP3ENC (gst_pad_get_parent (pad));
462 : 1 : peerpad = gst_pad_get_peer (GST_PAD (lame->sinkpad));
463 : :
464 [ + - ]: 1 : switch (GST_QUERY_TYPE (query)) {
465 : : case GST_QUERY_LATENCY:
466 : : {
467 [ + - ]: 1 : if ((res = gst_pad_query (peerpad, query))) {
468 : : gboolean live;
469 : : GstClockTime min_latency, max_latency;
470 : : gint64 latency;
471 : :
472 [ - + ]: 1 : if (lame->lgf == NULL)
473 : 0 : break;
474 : :
475 : 1 : gst_query_parse_latency (query, &live, &min_latency, &max_latency);
476 : :
477 : 1 : latency = gst_lamemp3enc_get_latency (lame);
478 : :
479 : : /* add our latency */
480 : 1 : min_latency += latency;
481 [ - + ]: 1 : if (max_latency != -1)
482 : 0 : max_latency += latency;
483 : :
484 : 1 : gst_query_set_latency (query, live, min_latency, max_latency);
485 : : }
486 : 1 : break;
487 : : }
488 : : default:
489 : 0 : res = gst_pad_query (peerpad, query);
490 : 0 : break;
491 : : }
492 : :
493 : 1 : gst_object_unref (peerpad);
494 : 1 : gst_object_unref (lame);
495 : 1 : return res;
496 : : }
497 : :
498 : : static void
499 : 4 : gst_lamemp3enc_init (GstLameMP3Enc * lame)
500 : : {
501 [ - + ]: 4 : GST_DEBUG_OBJECT (lame, "starting initialization");
502 : :
503 : 4 : lame->sinkpad =
504 : 4 : gst_pad_new_from_static_template (&gst_lamemp3enc_sink_template, "sink");
505 : 4 : gst_pad_set_event_function (lame->sinkpad,
506 : 4 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_sink_event));
507 : 4 : gst_pad_set_chain_function (lame->sinkpad,
508 : 4 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_chain));
509 : 4 : gst_pad_set_setcaps_function (lame->sinkpad,
510 : 4 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_sink_setcaps));
511 : 4 : gst_pad_set_getcaps_function (lame->sinkpad,
512 : 4 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_sink_getcaps));
513 : 4 : gst_element_add_pad (GST_ELEMENT (lame), lame->sinkpad);
514 : :
515 : 4 : lame->srcpad =
516 : 4 : gst_pad_new_from_static_template (&gst_lamemp3enc_src_template, "src");
517 : 4 : gst_pad_set_query_function (lame->srcpad,
518 : 4 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_src_query));
519 : 4 : gst_pad_set_setcaps_function (lame->srcpad,
520 : 4 : GST_DEBUG_FUNCPTR (gst_lamemp3enc_src_setcaps));
521 : 4 : gst_element_add_pad (GST_ELEMENT (lame), lame->srcpad);
522 : :
523 : 4 : lame->samplerate = 44100;
524 : 4 : lame->num_channels = 2;
525 : 4 : lame->setup = FALSE;
526 : :
527 : : /* Set default settings */
528 : 4 : lame->target = DEFAULT_TARGET;
529 : 4 : lame->bitrate = DEFAULT_BITRATE;
530 : 4 : lame->cbr = DEFAULT_CBR;
531 : 4 : lame->quality = DEFAULT_QUALITY;
532 : 4 : lame->encoding_engine_quality = DEFAULT_ENCODING_ENGINE_QUALITY;
533 : 4 : lame->mono = DEFAULT_MONO;
534 : :
535 [ - + ]: 4 : GST_DEBUG_OBJECT (lame, "done initializing");
536 : 4 : }
537 : :
538 : : /* <php-emulation-mode>three underscores for ___rate is really really really
539 : : * private as opposed to one underscore<php-emulation-mode> */
540 : : /* call this MACRO outside of the NULL state so that we have a higher chance
541 : : * of actually having a pipeline and bus to get the message through */
542 : :
543 : : #define CHECK_AND_FIXUP_BITRATE(obj,param,rate) \
544 : : G_STMT_START { \
545 : : gint ___rate = rate; \
546 : : gint maxrate = 320; \
547 : : gint multiplier = 64; \
548 : : if (rate == 0) { \
549 : : ___rate = rate; \
550 : : } else if (rate <= 64) { \
551 : : maxrate = 64; multiplier = 8; \
552 : : if ((rate % 8) != 0) ___rate = GST_ROUND_UP_8 (rate); \
553 : : } else if (rate <= 128) { \
554 : : maxrate = 128; multiplier = 16; \
555 : : if ((rate % 16) != 0) ___rate = GST_ROUND_UP_16 (rate); \
556 : : } else if (rate <= 256) { \
557 : : maxrate = 256; multiplier = 32; \
558 : : if ((rate % 32) != 0) ___rate = GST_ROUND_UP_32 (rate); \
559 : : } else if (rate <= 320) { \
560 : : maxrate = 320; multiplier = 64; \
561 : : if ((rate % 64) != 0) ___rate = GST_ROUND_UP_64 (rate); \
562 : : } \
563 : : if (___rate != rate) { \
564 : : GST_ELEMENT_WARNING (obj, LIBRARY, SETTINGS, \
565 : : (_("The requested bitrate %d kbit/s for property '%s' " \
566 : : "is not allowed. " \
567 : : "The bitrate was changed to %d kbit/s."), rate, \
568 : : param, ___rate), \
569 : : ("A bitrate below %d should be a multiple of %d.", \
570 : : maxrate, multiplier)); \
571 : : rate = ___rate; \
572 : : } \
573 : : } G_STMT_END
574 : :
575 : : static void
576 : 0 : gst_lamemp3enc_set_property (GObject * object, guint prop_id,
577 : : const GValue * value, GParamSpec * pspec)
578 : : {
579 : : GstLameMP3Enc *lame;
580 : :
581 : 0 : lame = GST_LAMEMP3ENC (object);
582 : :
583 [ # # # # : 0 : switch (prop_id) {
# # # ]
584 : : case ARG_TARGET:
585 : 0 : lame->target = g_value_get_enum (value);
586 : 0 : break;
587 : : case ARG_BITRATE:
588 : 0 : lame->bitrate = g_value_get_int (value);
589 : 0 : break;
590 : : case ARG_CBR:
591 : 0 : lame->cbr = g_value_get_boolean (value);
592 : 0 : break;
593 : : case ARG_QUALITY:
594 : 0 : lame->quality = g_value_get_float (value);
595 : 0 : break;
596 : : case ARG_ENCODING_ENGINE_QUALITY:
597 : 0 : lame->encoding_engine_quality = g_value_get_enum (value);
598 : 0 : break;
599 : : case ARG_MONO:
600 : 0 : lame->mono = g_value_get_boolean (value);
601 : 0 : break;
602 : : default:
603 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
604 : 0 : break;
605 : : }
606 : 0 : }
607 : :
608 : : static void
609 : 0 : gst_lamemp3enc_get_property (GObject * object, guint prop_id, GValue * value,
610 : : GParamSpec * pspec)
611 : : {
612 : : GstLameMP3Enc *lame;
613 : :
614 : 0 : lame = GST_LAMEMP3ENC (object);
615 : :
616 [ # # # # : 0 : switch (prop_id) {
# # # ]
617 : : case ARG_TARGET:
618 : 0 : g_value_set_enum (value, lame->target);
619 : 0 : break;
620 : : case ARG_BITRATE:
621 : 0 : g_value_set_int (value, lame->bitrate);
622 : 0 : break;
623 : : case ARG_CBR:
624 : 0 : g_value_set_boolean (value, lame->cbr);
625 : 0 : break;
626 : : case ARG_QUALITY:
627 : 0 : g_value_set_float (value, lame->quality);
628 : 0 : break;
629 : : case ARG_ENCODING_ENGINE_QUALITY:
630 : 0 : g_value_set_enum (value, lame->encoding_engine_quality);
631 : 0 : break;
632 : : case ARG_MONO:
633 : 0 : g_value_set_boolean (value, lame->mono);
634 : 0 : break;
635 : : default:
636 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
637 : 0 : break;
638 : : }
639 : 0 : }
640 : :
641 : : static gboolean
642 : 3 : gst_lamemp3enc_sink_event (GstPad * pad, GstEvent * event)
643 : : {
644 : : gboolean ret;
645 : : GstLameMP3Enc *lame;
646 : :
647 : 3 : lame = GST_LAMEMP3ENC (gst_pad_get_parent (pad));
648 : :
649 [ + - - + : 3 : switch (GST_EVENT_TYPE (event)) {
+ ]
650 : : case GST_EVENT_EOS:{
651 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "handling EOS event");
652 : :
653 [ + - ]: 1 : if (lame->lgf != NULL) {
654 : : GstBuffer *buf;
655 : : gint size;
656 : :
657 : 1 : buf = gst_buffer_new_and_alloc (7200);
658 : 1 : size = lame_encode_flush (lame->lgf, GST_BUFFER_DATA (buf), 7200);
659 : :
660 [ + - ][ + - ]: 2 : if (size > 0 && lame->last_flow == GST_FLOW_OK) {
661 : : gint64 duration;
662 : :
663 : 1 : duration = gst_util_uint64_scale (size, 8 * GST_SECOND,
664 : 1 : 1000 * lame->bitrate);
665 : :
666 [ - + ]: 1 : if (lame->last_ts == GST_CLOCK_TIME_NONE) {
667 : 0 : lame->last_ts = lame->eos_ts;
668 : 0 : lame->last_duration = duration;
669 : : } else {
670 : 1 : lame->last_duration += duration;
671 : : }
672 : :
673 : 1 : GST_BUFFER_TIMESTAMP (buf) = lame->last_ts;
674 : 1 : GST_BUFFER_DURATION (buf) = lame->last_duration;
675 : 1 : lame->last_ts = GST_CLOCK_TIME_NONE;
676 : 1 : GST_BUFFER_SIZE (buf) = size;
677 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "pushing final packet of %u bytes", size);
678 : 1 : gst_buffer_set_caps (buf, GST_PAD_CAPS (lame->srcpad));
679 : 1 : gst_pad_push (lame->srcpad, buf);
680 : : } else {
681 [ # # ]: 0 : GST_DEBUG_OBJECT (lame, "no final packet (size=%d, last_flow=%s)",
682 : : size, gst_flow_get_name (lame->last_flow));
683 : 0 : gst_buffer_unref (buf);
684 : : }
685 : : }
686 : :
687 : 1 : ret = gst_pad_event_default (pad, event);
688 : 1 : break;
689 : : }
690 : : case GST_EVENT_FLUSH_START:
691 [ # # ]: 0 : GST_DEBUG_OBJECT (lame, "handling FLUSH start event");
692 : : /* forward event */
693 : 0 : ret = gst_pad_push_event (lame->srcpad, event);
694 : 0 : break;
695 : : case GST_EVENT_FLUSH_STOP:
696 : : {
697 : 0 : guchar *mp3_data = NULL;
698 : : gint mp3_buffer_size;
699 : :
700 [ # # ]: 0 : GST_DEBUG_OBJECT (lame, "handling FLUSH stop event");
701 : :
702 [ # # ]: 0 : if (lame->lgf) {
703 : : /* clear buffers if we already have lame set up */
704 : 0 : mp3_buffer_size = 7200;
705 : 0 : mp3_data = g_malloc (mp3_buffer_size);
706 : 0 : lame_encode_flush (lame->lgf, mp3_data, mp3_buffer_size);
707 : 0 : g_free (mp3_data);
708 : : }
709 : :
710 : 0 : ret = gst_pad_push_event (lame->srcpad, event);
711 : 0 : break;
712 : : }
713 : : case GST_EVENT_TAG:
714 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "ignoring TAG event, passing it on");
715 : 1 : ret = gst_pad_push_event (lame->srcpad, event);
716 : 1 : break;
717 : : default:
718 : 1 : ret = gst_pad_event_default (pad, event);
719 : 1 : break;
720 : : }
721 : 3 : gst_object_unref (lame);
722 : 3 : return ret;
723 : : }
724 : :
725 : : static GstFlowReturn
726 : 2 : gst_lamemp3enc_chain (GstPad * pad, GstBuffer * buf)
727 : : {
728 : : GstLameMP3Enc *lame;
729 : : guchar *mp3_data;
730 : : gint mp3_buffer_size, mp3_size;
731 : : gint64 duration;
732 : : GstFlowReturn result;
733 : : gint num_samples;
734 : : guint8 *data;
735 : : guint size;
736 : :
737 : 2 : lame = GST_LAMEMP3ENC (GST_PAD_PARENT (pad));
738 : :
739 [ - + ]: 2 : GST_LOG_OBJECT (lame, "entered chain");
740 : :
741 [ - + ]: 2 : if (!lame->setup)
742 : 0 : goto not_setup;
743 : :
744 : 2 : data = GST_BUFFER_DATA (buf);
745 : 2 : size = GST_BUFFER_SIZE (buf);
746 : :
747 : 2 : num_samples = size / 2;
748 : :
749 : : /* allocate space for output */
750 : 2 : mp3_buffer_size = 1.25 * num_samples + 7200;
751 : 2 : mp3_data = g_malloc (mp3_buffer_size);
752 : :
753 : : /* lame seems to be too stupid to get mono interleaved going */
754 [ + - ]: 2 : if (lame->num_channels == 1) {
755 : 2 : mp3_size = lame_encode_buffer (lame->lgf,
756 : : (short int *) data,
757 : : (short int *) data, num_samples, mp3_data, mp3_buffer_size);
758 : : } else {
759 : 0 : mp3_size = lame_encode_buffer_interleaved (lame->lgf,
760 : : (short int *) data,
761 : 0 : num_samples / lame->num_channels, mp3_data, mp3_buffer_size);
762 : : }
763 : :
764 [ - + ]: 2 : GST_LOG_OBJECT (lame, "encoded %d bytes of audio to %d bytes of mp3",
765 : : size, mp3_size);
766 : :
767 : 2 : duration = gst_util_uint64_scale_int (size, GST_SECOND,
768 : 2 : 2 * lame->samplerate * lame->num_channels);
769 : :
770 [ + - ][ + + ]: 2 : if (GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE &&
771 : 2 : GST_BUFFER_DURATION (buf) != duration) {
772 [ - + ][ # # ]: 1 : GST_DEBUG_OBJECT (lame, "incoming buffer had incorrect duration %"
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
773 : : GST_TIME_FORMAT ", outgoing buffer will have correct duration %"
774 : : GST_TIME_FORMAT,
775 : : GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_TIME_ARGS (duration));
776 : : }
777 : :
778 [ + + ]: 2 : if (lame->last_ts == GST_CLOCK_TIME_NONE) {
779 : 1 : lame->last_ts = GST_BUFFER_TIMESTAMP (buf);
780 : 1 : lame->last_offs = GST_BUFFER_OFFSET (buf);
781 : 1 : lame->last_duration = duration;
782 : : } else {
783 : 1 : lame->last_duration += duration;
784 : : }
785 : :
786 : 2 : gst_buffer_unref (buf);
787 : :
788 [ - + ]: 2 : if (mp3_size < 0) {
789 : 0 : g_warning ("error %d", mp3_size);
790 : : }
791 : :
792 [ - + ]: 2 : if (mp3_size > 0) {
793 : : GstBuffer *outbuf;
794 : :
795 : 0 : outbuf = gst_buffer_new ();
796 : 0 : GST_BUFFER_DATA (outbuf) = mp3_data;
797 : 0 : GST_BUFFER_MALLOCDATA (outbuf) = mp3_data;
798 : 0 : GST_BUFFER_SIZE (outbuf) = mp3_size;
799 : 0 : GST_BUFFER_TIMESTAMP (outbuf) = lame->last_ts;
800 : 0 : GST_BUFFER_OFFSET (outbuf) = lame->last_offs;
801 : 0 : GST_BUFFER_DURATION (outbuf) = lame->last_duration;
802 : 0 : gst_buffer_set_caps (outbuf, GST_PAD_CAPS (lame->srcpad));
803 : :
804 : 0 : result = gst_pad_push (lame->srcpad, outbuf);
805 : 0 : lame->last_flow = result;
806 [ # # ]: 0 : if (result != GST_FLOW_OK) {
807 [ # # ]: 0 : GST_DEBUG_OBJECT (lame, "flow return: %s", gst_flow_get_name (result));
808 : : }
809 : :
810 [ # # ]: 0 : if (GST_CLOCK_TIME_IS_VALID (lame->last_ts))
811 : 0 : lame->eos_ts = lame->last_ts + lame->last_duration;
812 : : else
813 : 0 : lame->eos_ts = GST_CLOCK_TIME_NONE;
814 : 0 : lame->last_ts = GST_CLOCK_TIME_NONE;
815 : : } else {
816 : 2 : g_free (mp3_data);
817 : 2 : result = GST_FLOW_OK;
818 : : }
819 : :
820 : 2 : return result;
821 : :
822 : : /* ERRORS */
823 : : not_setup:
824 : : {
825 : 0 : gst_buffer_unref (buf);
826 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (lame, CORE, NEGOTIATION, (NULL),
[ # # ][ # # ]
827 : : ("encoder not initialized (input is not audio?)"));
828 : 2 : return GST_FLOW_ERROR;
829 : : }
830 : : }
831 : :
832 : : /* set up the encoder state */
833 : : static gboolean
834 : 1 : gst_lamemp3enc_setup (GstLameMP3Enc * lame)
835 : : {
836 : :
837 : : #define CHECK_ERROR(command) G_STMT_START {\
838 : : if ((command) < 0) { \
839 : : GST_ERROR_OBJECT (lame, "setup failed: " G_STRINGIFY (command)); \
840 : : return FALSE; \
841 : : } \
842 : : }G_STMT_END
843 : :
844 : : int retval;
845 : : GstCaps *allowed_caps;
846 : :
847 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "starting setup");
848 : :
849 : : /* check if we're already setup; if we are, we might want to check
850 : : * if this initialization is compatible with the previous one */
851 : : /* FIXME: do this */
852 [ - + ]: 1 : if (lame->setup) {
853 [ # # ]: 0 : GST_WARNING_OBJECT (lame, "already setup");
854 : 0 : lame->setup = FALSE;
855 : : }
856 : :
857 : 1 : lame->lgf = lame_init ();
858 : :
859 [ - + ]: 1 : if (lame->lgf == NULL)
860 : 0 : return FALSE;
861 : :
862 : : /* post latency message on the bus */
863 : 1 : gst_element_post_message (GST_ELEMENT (lame),
864 : 1 : gst_message_new_latency (GST_OBJECT (lame)));
865 : :
866 : : /* copy the parameters over */
867 : 1 : lame_set_in_samplerate (lame->lgf, lame->samplerate);
868 : :
869 : : /* let lame choose default samplerate unless outgoing sample rate is fixed */
870 : 1 : allowed_caps = gst_pad_get_allowed_caps (lame->srcpad);
871 : :
872 [ + - ]: 1 : if (allowed_caps != NULL) {
873 : : GstStructure *structure;
874 : : gint samplerate;
875 : :
876 : 1 : structure = gst_caps_get_structure (allowed_caps, 0);
877 : :
878 [ - + ]: 1 : if (gst_structure_get_int (structure, "rate", &samplerate)) {
879 [ # # ]: 0 : GST_DEBUG_OBJECT (lame, "Setting sample rate to %d as fixed in src caps",
880 : : samplerate);
881 : 0 : lame_set_out_samplerate (lame->lgf, samplerate);
882 : : } else {
883 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "Letting lame choose sample rate");
884 : 1 : lame_set_out_samplerate (lame->lgf, 0);
885 : : }
886 : 1 : gst_caps_unref (allowed_caps);
887 : 1 : allowed_caps = NULL;
888 : : } else {
889 [ # # ]: 0 : GST_DEBUG_OBJECT (lame, "No peer yet, letting lame choose sample rate");
890 : 0 : lame_set_out_samplerate (lame->lgf, 0);
891 : : }
892 : :
893 [ - + ][ # # ]: 1 : CHECK_ERROR (lame_set_num_channels (lame->lgf, lame->num_channels));
894 [ - + ][ # # ]: 1 : CHECK_ERROR (lame_set_bWriteVbrTag (lame->lgf, 0));
895 : :
896 [ + - ]: 1 : if (lame->target == LAMEMP3ENC_TARGET_QUALITY) {
897 [ - + ][ # # ]: 1 : CHECK_ERROR (lame_set_VBR (lame->lgf, vbr_default));
898 [ - + ][ # # ]: 1 : CHECK_ERROR (lame_set_VBR_quality (lame->lgf, lame->quality));
899 : : } else {
900 [ # # ]: 0 : if (lame->cbr) {
901 [ # # ][ # # ]: 0 : CHECK_AND_FIXUP_BITRATE (lame, "bitrate", lame->bitrate);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
902 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_VBR (lame->lgf, vbr_off));
903 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_brate (lame->lgf, lame->bitrate));
904 : : } else {
905 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_VBR (lame->lgf, vbr_abr));
906 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_VBR_mean_bitrate_kbps (lame->lgf, lame->bitrate));
907 : : }
908 : : }
909 : :
910 [ - + ]: 1 : if (lame->encoding_engine_quality == LAMEMP3ENC_ENCODING_ENGINE_QUALITY_FAST)
911 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_quality (lame->lgf, 7));
912 [ - + ]: 1 : else if (lame->encoding_engine_quality ==
913 : : LAMEMP3ENC_ENCODING_ENGINE_QUALITY_HIGH)
914 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_quality (lame->lgf, 2));
915 : : /* else default */
916 : :
917 [ - + ]: 1 : if (lame->mono)
918 [ # # ][ # # ]: 0 : CHECK_ERROR (lame_set_mode (lame->lgf, MONO));
919 : :
920 : : /* initialize the lame encoder */
921 [ + - ]: 1 : if ((retval = lame_init_params (lame->lgf)) >= 0) {
922 : 1 : lame->setup = TRUE;
923 : : /* FIXME: it would be nice to print out the mode here */
924 [ - + ][ # # ]: 1 : GST_INFO
925 : : ("lame encoder setup (target %s, quality %f, bitrate %d, %d Hz, %d channels)",
926 : : (lame->target == LAMEMP3ENC_TARGET_QUALITY) ? "quality" : "bitrate",
927 : : lame->quality, lame->bitrate, lame->samplerate, lame->num_channels);
928 : : } else {
929 [ # # ]: 0 : GST_ERROR_OBJECT (lame, "lame_init_params returned %d", retval);
930 : : }
931 : :
932 [ - + ]: 1 : GST_DEBUG_OBJECT (lame, "done with setup");
933 : :
934 : 1 : return lame->setup;
935 : : #undef CHECK_ERROR
936 : : }
937 : :
938 : : static GstStateChangeReturn
939 : 40 : gst_lamemp3enc_change_state (GstElement * element, GstStateChange transition)
940 : : {
941 : : GstLameMP3Enc *lame;
942 : : GstStateChangeReturn result;
943 : :
944 : 40 : lame = GST_LAMEMP3ENC (element);
945 : :
946 [ + + ]: 40 : switch (transition) {
947 : : case GST_STATE_CHANGE_READY_TO_PAUSED:
948 : 8 : lame->last_flow = GST_FLOW_OK;
949 : 8 : lame->last_ts = GST_CLOCK_TIME_NONE;
950 : 8 : lame->eos_ts = GST_CLOCK_TIME_NONE;
951 : 8 : break;
952 : : default:
953 : 32 : break;
954 : : }
955 : :
956 : 40 : result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
957 : :
958 [ + + ]: 40 : switch (transition) {
959 : : case GST_STATE_CHANGE_READY_TO_NULL:
960 : 5 : gst_lamemp3enc_release_memory (lame);
961 : 5 : break;
962 : : default:
963 : 35 : break;
964 : : }
965 : :
966 : 40 : return result;
967 : : }
968 : :
969 : : gboolean
970 : 7 : gst_lamemp3enc_register (GstPlugin * plugin)
971 : : {
972 [ + - ]: 7 : GST_DEBUG_CATEGORY_INIT (debug, "lamemp3enc", 0, "lame mp3 encoder");
973 : :
974 [ - + ]: 7 : if (!gst_element_register (plugin, "lamemp3enc", GST_RANK_PRIMARY,
975 : : GST_TYPE_LAMEMP3ENC))
976 : 0 : return FALSE;
977 : :
978 : 7 : return TRUE;
979 : : }
|