Added sl for Slovenian translation
[banshee.git] / libbanshee / banshee-transcoder.c
blob6e2bd7b7536532ffed7c6f79bc6eeadbbe891e58
1 //
2 // banshee-transcoder.c
3 //
4 // Author:
5 // Aaron Bockover <abockover@novell.com>
6 //
7 // Copyright (C) 2005-2008 Novell, Inc.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 #include <gst/gst.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <glib.h>
33 #include <glib/gi18n.h>
34 #include <glib/gstdio.h>
36 typedef struct GstTranscoder GstTranscoder;
38 typedef void (* GstTranscoderProgressCallback) (GstTranscoder *transcoder, gdouble progress);
39 typedef void (* GstTranscoderFinishedCallback) (GstTranscoder *transcoder);
40 typedef void (* GstTranscoderErrorCallback) (GstTranscoder *transcoder, const gchar *error, const gchar *debug);
42 struct GstTranscoder {
43 gboolean is_transcoding;
44 guint iterate_timeout_id;
45 GstElement *pipeline;
46 GstElement *sink_bin;
47 GstElement *conv_elem;
48 gchar *output_uri;
49 GstTranscoderProgressCallback progress_cb;
50 GstTranscoderFinishedCallback finished_cb;
51 GstTranscoderErrorCallback error_cb;
54 // private methods
56 static void
57 gst_transcoder_raise_error(GstTranscoder *transcoder, const gchar *error, const gchar *debug)
59 g_return_if_fail(transcoder != NULL);
60 g_return_if_fail(transcoder->error_cb != NULL);
62 transcoder->error_cb(transcoder, error, debug);
65 static gboolean
66 gst_transcoder_iterate_timeout(GstTranscoder *transcoder)
68 GstFormat format = GST_FORMAT_TIME;
69 gint64 position;
70 gint64 duration;
72 g_return_val_if_fail(transcoder != NULL, FALSE);
74 if(!gst_element_query_duration(transcoder->pipeline, &format, &duration) ||
75 !gst_element_query_position(transcoder->sink_bin, &format, &position)) {
76 return TRUE;
79 if(transcoder->progress_cb != NULL) {
80 transcoder->progress_cb(transcoder, (double)position / (double)duration);
83 return TRUE;
86 static void
87 gst_transcoder_start_iterate_timeout(GstTranscoder *transcoder)
89 g_return_if_fail(transcoder != NULL);
91 if(transcoder->iterate_timeout_id != 0) {
92 return;
95 transcoder->iterate_timeout_id = g_timeout_add(200,
96 (GSourceFunc)gst_transcoder_iterate_timeout, transcoder);
99 static void
100 gst_transcoder_stop_iterate_timeout(GstTranscoder *transcoder)
102 g_return_if_fail(transcoder != NULL);
104 if(transcoder->iterate_timeout_id == 0) {
105 return;
108 g_source_remove(transcoder->iterate_timeout_id);
109 transcoder->iterate_timeout_id = 0;
112 static gboolean
113 gst_transcoder_bus_callback(GstBus *bus, GstMessage *message, gpointer data)
115 GstTranscoder *transcoder = (GstTranscoder *)data;
117 g_return_val_if_fail(transcoder != NULL, FALSE);
119 switch(GST_MESSAGE_TYPE(message)) {
120 case GST_MESSAGE_ERROR: {
121 GError *error;
122 gchar *debug;
124 transcoder->is_transcoding = FALSE;
125 gst_transcoder_stop_iterate_timeout(transcoder);
127 if(transcoder->error_cb != NULL) {
128 gst_message_parse_error(message, &error, &debug);
129 gst_transcoder_raise_error(transcoder, error->message, debug);
130 g_error_free(error);
131 g_free(debug);
134 break;
136 case GST_MESSAGE_EOS:
137 gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_NULL);
138 g_object_unref(G_OBJECT(transcoder->pipeline));
139 transcoder->pipeline = NULL;
141 transcoder->is_transcoding = FALSE;
142 gst_transcoder_stop_iterate_timeout(transcoder);
145 FIXME: Replace with regular stat
146 GnomeVFSFileInfo fileinfo;
147 if(gnome_vfs_get_file_info(transcoder->output_uri, &fileinfo,
148 GNOME_VFS_FILE_INFO_DEFAULT) == GNOME_VFS_OK) {
149 if(fileinfo.size < 100) {
150 gst_transcoder_raise_error(transcoder,
151 _("No decoder could be found for source format."), NULL);
152 g_remove(transcoder->output_uri);
153 break;
155 } else {
156 gst_transcoder_raise_error(transcoder, _("Could not stat encoded file"), NULL);
157 break;
160 if(transcoder->finished_cb != NULL) {
161 transcoder->finished_cb(transcoder);
163 break;
164 default:
165 break;
168 return TRUE;
171 static GstElement *
172 gst_transcoder_build_encoder(const gchar *encoder_pipeline)
174 GstElement *encoder = NULL;
175 gchar *pipeline;
176 GError *error = NULL;
178 pipeline = g_strdup_printf("%s", encoder_pipeline);
179 encoder = gst_parse_bin_from_description(pipeline, TRUE, &error);
180 g_free(pipeline);
182 if(error != NULL) {
183 return NULL;
186 return encoder;
189 static void
190 gst_transcoder_new_decoded_pad(GstElement *decodebin, GstPad *pad,
191 gboolean last, gpointer data)
193 GstCaps *caps;
194 GstStructure *str;
195 GstPad *audiopad;
196 GstTranscoder *transcoder = (GstTranscoder *)data;
198 g_return_if_fail(transcoder != NULL);
200 audiopad = gst_element_get_pad(transcoder->sink_bin, "sink");
202 if(GST_PAD_IS_LINKED(audiopad)) {
203 g_object_unref(audiopad);
204 return;
207 caps = gst_pad_get_caps(pad);
208 str = gst_caps_get_structure(caps, 0);
210 if(!g_strrstr(gst_structure_get_name(str), "audio")) {
211 gst_caps_unref(caps);
212 gst_object_unref(audiopad);
213 return;
216 gst_caps_unref(caps);
217 gst_pad_link(pad, audiopad);
220 static gboolean
221 gst_transcoder_create_pipeline(GstTranscoder *transcoder,
222 const char *input_uri, const char *output_uri,
223 const gchar *encoder_pipeline)
225 GstElement *source_elem;
226 GstElement *decoder_elem;
227 GstElement *encoder_elem;
228 GstElement *sink_elem;
229 GstElement *conv_elem;
230 GstPad *encoder_pad;
232 if(transcoder == NULL) {
233 return FALSE;
236 transcoder->pipeline = gst_pipeline_new("pipeline");
238 source_elem = gst_element_make_from_uri(GST_URI_SRC, input_uri, "source");
239 if(source_elem == NULL) {
240 gst_transcoder_raise_error(transcoder, _("Could not create source element"), NULL);
241 return FALSE;
244 decoder_elem = gst_element_factory_make("decodebin2", "decodebin2");
245 if(decoder_elem == NULL) {
246 gst_transcoder_raise_error(transcoder, _("Could not create 'decodebin2' plugin"), NULL);
247 return FALSE;
250 sink_elem = gst_element_make_from_uri(GST_URI_SINK, output_uri, "sink");
251 if(sink_elem == NULL) {
252 gst_transcoder_raise_error(transcoder, _("Could not create sink element"), NULL);
253 return FALSE;
256 transcoder->sink_bin = gst_bin_new("sinkbin");
257 if(transcoder->sink_bin == NULL) {
258 gst_transcoder_raise_error(transcoder, _("Could not create 'sinkben' plugin"), NULL);
259 return FALSE;
262 conv_elem = gst_element_factory_make("audioconvert", "audioconvert");
263 if(conv_elem == NULL) {
264 gst_transcoder_raise_error(transcoder, _("Could not create 'audioconvert' plugin"), NULL);
265 return FALSE;
268 encoder_elem = gst_transcoder_build_encoder(encoder_pipeline);
269 if(encoder_elem == NULL) {
270 gst_transcoder_raise_error(transcoder, _("Could not create encoding pipeline"), encoder_pipeline);
271 return FALSE;
274 encoder_pad = gst_element_get_pad(conv_elem, "sink");
275 if(encoder_pad == NULL) {
276 gst_transcoder_raise_error(transcoder, _("Could not get sink pad from encoder"), NULL);
277 return FALSE;
280 gst_bin_add_many(GST_BIN(transcoder->sink_bin), conv_elem, encoder_elem, sink_elem, NULL);
281 gst_element_link_many(conv_elem, encoder_elem, sink_elem, NULL);
283 gst_element_add_pad(transcoder->sink_bin, gst_ghost_pad_new("sink", encoder_pad));
284 gst_object_unref(encoder_pad);
286 gst_bin_add_many(GST_BIN(transcoder->pipeline), source_elem, decoder_elem,
287 transcoder->sink_bin, NULL);
289 gst_element_link(source_elem, decoder_elem);
291 g_signal_connect(decoder_elem, "new-decoded-pad",
292 G_CALLBACK(gst_transcoder_new_decoded_pad), transcoder);
294 gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(transcoder->pipeline)),
295 gst_transcoder_bus_callback, transcoder);
297 transcoder->conv_elem = conv_elem;
299 return TRUE;
302 // public methods
304 GstTranscoder *
305 gst_transcoder_new ()
307 return g_new0 (GstTranscoder, 1);
310 void
311 gst_transcoder_free(GstTranscoder *transcoder)
313 g_return_if_fail(transcoder != NULL);
314 gst_transcoder_stop_iterate_timeout(transcoder);
316 if(GST_IS_ELEMENT(transcoder->pipeline)) {
317 gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_NULL);
318 gst_object_unref(GST_OBJECT(transcoder->pipeline));
321 if(transcoder->output_uri != NULL) {
322 g_free(transcoder->output_uri);
323 transcoder->output_uri = NULL;
326 g_free(transcoder);
327 transcoder = NULL;
330 void
331 gst_transcoder_transcode(GstTranscoder *transcoder, const gchar *input_uri,
332 const gchar *output_uri, const gchar *encoder_pipeline)
334 g_return_if_fail(transcoder != NULL);
336 if(transcoder->is_transcoding) {
337 return;
340 if(!gst_transcoder_create_pipeline(transcoder, input_uri, output_uri, encoder_pipeline)) {
341 gst_transcoder_raise_error(transcoder, _("Could not construct pipeline"), NULL);
342 return;
345 if(transcoder->output_uri != NULL) {
346 g_free(transcoder->output_uri);
349 transcoder->output_uri = g_strdup(output_uri);
350 transcoder->is_transcoding = TRUE;
352 gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_PLAYING);
353 gst_transcoder_start_iterate_timeout(transcoder);
356 void
357 gst_transcoder_cancel(GstTranscoder *transcoder)
359 g_return_if_fail(transcoder != NULL);
360 gst_transcoder_stop_iterate_timeout(transcoder);
362 transcoder->is_transcoding = FALSE;
364 if(GST_IS_ELEMENT(transcoder->pipeline)) {
365 gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_NULL);
366 gst_object_unref(GST_OBJECT(transcoder->pipeline));
369 g_remove(transcoder->output_uri);
372 void
373 gst_transcoder_set_progress_callback(GstTranscoder *transcoder,
374 GstTranscoderProgressCallback cb)
376 g_return_if_fail(transcoder != NULL);
377 transcoder->progress_cb = cb;
380 void
381 gst_transcoder_set_finished_callback(GstTranscoder *transcoder,
382 GstTranscoderFinishedCallback cb)
384 g_return_if_fail(transcoder != NULL);
385 transcoder->finished_cb = cb;
388 void
389 gst_transcoder_set_error_callback(GstTranscoder *transcoder,
390 GstTranscoderErrorCallback cb)
392 g_return_if_fail(transcoder != NULL);
393 transcoder->error_cb = cb;
396 gboolean
397 gst_transcoder_get_is_transcoding(GstTranscoder *transcoder)
399 g_return_val_if_fail(transcoder != NULL, FALSE);
400 return transcoder->is_transcoding;