2 // banshee-transcoder.c
5 // Aaron Bockover <abockover@novell.com>
7 // Copyright (C) 2005-2008 Novell, Inc.
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.
31 #include <sys/types.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
;
47 GstElement
*conv_elem
;
49 GstTranscoderProgressCallback progress_cb
;
50 GstTranscoderFinishedCallback finished_cb
;
51 GstTranscoderErrorCallback error_cb
;
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
);
66 gst_transcoder_iterate_timeout(GstTranscoder
*transcoder
)
68 GstFormat format
= GST_FORMAT_TIME
;
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
)) {
79 if(transcoder
->progress_cb
!= NULL
) {
80 transcoder
->progress_cb(transcoder
, (double)position
/ (double)duration
);
87 gst_transcoder_start_iterate_timeout(GstTranscoder
*transcoder
)
89 g_return_if_fail(transcoder
!= NULL
);
91 if(transcoder
->iterate_timeout_id
!= 0) {
95 transcoder
->iterate_timeout_id
= g_timeout_add(200,
96 (GSourceFunc
)gst_transcoder_iterate_timeout
, transcoder
);
100 gst_transcoder_stop_iterate_timeout(GstTranscoder
*transcoder
)
102 g_return_if_fail(transcoder
!= NULL
);
104 if(transcoder
->iterate_timeout_id
== 0) {
108 g_source_remove(transcoder
->iterate_timeout_id
);
109 transcoder
->iterate_timeout_id
= 0;
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
: {
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
);
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);
156 gst_transcoder_raise_error(transcoder, _("Could not stat encoded file"), NULL);
160 if(transcoder
->finished_cb
!= NULL
) {
161 transcoder
->finished_cb(transcoder
);
172 gst_transcoder_build_encoder(const gchar
*encoder_pipeline
)
174 GstElement
*encoder
= NULL
;
176 GError
*error
= NULL
;
178 pipeline
= g_strdup_printf("%s", encoder_pipeline
);
179 encoder
= gst_parse_bin_from_description(pipeline
, TRUE
, &error
);
190 gst_transcoder_new_decoded_pad(GstElement
*decodebin
, GstPad
*pad
,
191 gboolean last
, gpointer data
)
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
);
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
);
216 gst_caps_unref(caps
);
217 gst_pad_link(pad
, audiopad
);
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
;
232 if(transcoder
== NULL
) {
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
;
305 gst_transcoder_new ()
307 return g_new0 (GstTranscoder
, 1);
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
;
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
) {
340 if(!gst_transcoder_create_pipeline(transcoder
, input_uri
, output_uri
, encoder_pipeline
)) {
341 gst_transcoder_raise_error(transcoder
, _("Could not construct pipeline"), NULL
);
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
);
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
);
373 gst_transcoder_set_progress_callback(GstTranscoder
*transcoder
,
374 GstTranscoderProgressCallback cb
)
376 g_return_if_fail(transcoder
!= NULL
);
377 transcoder
->progress_cb
= cb
;
381 gst_transcoder_set_finished_callback(GstTranscoder
*transcoder
,
382 GstTranscoderFinishedCallback cb
)
384 g_return_if_fail(transcoder
!= NULL
);
385 transcoder
->finished_cb
= cb
;
389 gst_transcoder_set_error_callback(GstTranscoder
*transcoder
,
390 GstTranscoderErrorCallback cb
)
392 g_return_if_fail(transcoder
!= NULL
);
393 transcoder
->error_cb
= cb
;
397 gst_transcoder_get_is_transcoding(GstTranscoder
*transcoder
)
399 g_return_val_if_fail(transcoder
!= NULL
, FALSE
);
400 return transcoder
->is_transcoding
;