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;
48
GstTranscoderProgressCallback progress_cb;
49
GstTranscoderFinishedCallback finished_cb;
50
GstTranscoderErrorCallback error_cb;
56
gst_transcoder_raise_error(GstTranscoder *transcoder, const gchar *error, const gchar *debug)
58
g_return_if_fail(transcoder != NULL);
59
g_return_if_fail(transcoder->error_cb != NULL);
61
transcoder->error_cb(transcoder, error, debug);
65
gst_transcoder_iterate_timeout(GstTranscoder *transcoder)
67
GstFormat format = GST_FORMAT_TIME;
71
g_return_val_if_fail(transcoder != NULL, FALSE);
73
if(!gst_element_query_duration(transcoder->pipeline, &format, &duration) ||
74
!gst_element_query_position(transcoder->sink_bin, &format, &position)) {
78
if(transcoder->progress_cb != NULL) {
79
transcoder->progress_cb(transcoder, (double)position / (double)duration);
86
gst_transcoder_start_iterate_timeout(GstTranscoder *transcoder)
88
g_return_if_fail(transcoder != NULL);
90
if(transcoder->iterate_timeout_id != 0) {
94
transcoder->iterate_timeout_id = g_timeout_add(200,
95
(GSourceFunc)gst_transcoder_iterate_timeout, transcoder);
99
gst_transcoder_stop_iterate_timeout(GstTranscoder *transcoder)
101
g_return_if_fail(transcoder != NULL);
103
if(transcoder->iterate_timeout_id == 0) {
107
g_source_remove(transcoder->iterate_timeout_id);
108
transcoder->iterate_timeout_id = 0;
112
gst_transcoder_bus_callback(GstBus *bus, GstMessage *message, gpointer data)
114
GstTranscoder *transcoder = (GstTranscoder *)data;
116
g_return_val_if_fail(transcoder != NULL, FALSE);
118
switch(GST_MESSAGE_TYPE(message)) {
119
case GST_MESSAGE_ERROR: {
123
transcoder->is_transcoding = FALSE;
124
gst_transcoder_stop_iterate_timeout(transcoder);
126
if(transcoder->error_cb != NULL) {
127
gst_message_parse_error(message, &error, &debug);
128
gst_transcoder_raise_error(transcoder, error->message, debug);
135
case GST_MESSAGE_EOS:
136
gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_NULL);
137
g_object_unref(G_OBJECT(transcoder->pipeline));
138
transcoder->pipeline = NULL;
140
transcoder->is_transcoding = FALSE;
141
gst_transcoder_stop_iterate_timeout(transcoder);
144
FIXME: Replace with regular stat
145
GnomeVFSFileInfo fileinfo;
146
if(gnome_vfs_get_file_info(transcoder->output_uri, &fileinfo,
147
GNOME_VFS_FILE_INFO_DEFAULT) == GNOME_VFS_OK) {
148
if(fileinfo.size < 100) {
149
gst_transcoder_raise_error(transcoder,
150
_("No decoder could be found for source format."), NULL);
151
g_remove(transcoder->output_uri);
155
gst_transcoder_raise_error(transcoder, _("Could not stat encoded file"), NULL);
159
if(transcoder->finished_cb != NULL) {
160
transcoder->finished_cb(transcoder);
171
gst_transcoder_build_encoder(const gchar *encoder_pipeline)
173
GstElement *encoder = NULL;
175
GError *error = NULL;
177
pipeline = g_strdup_printf("%s", encoder_pipeline);
178
encoder = gst_parse_bin_from_description(pipeline, TRUE, &error);
189
gst_transcoder_new_decoded_pad(GstElement *decodebin, GstPad *pad,
190
gboolean last, gpointer data)
195
GstTranscoder *transcoder = (GstTranscoder *)data;
197
g_return_if_fail(transcoder != NULL);
199
audiopad = gst_element_get_pad(transcoder->sink_bin, "sink");
201
if(GST_PAD_IS_LINKED(audiopad)) {
202
g_object_unref(audiopad);
206
caps = gst_pad_get_caps(pad);
207
str = gst_caps_get_structure(caps, 0);
209
if(!g_strrstr(gst_structure_get_name(str), "audio")) {
210
gst_caps_unref(caps);
211
gst_object_unref(audiopad);
215
gst_caps_unref(caps);
216
gst_pad_link(pad, audiopad);
220
gst_transcoder_create_pipeline(GstTranscoder *transcoder,
221
const char *input_uri, const char *output_uri,
222
const gchar *encoder_pipeline)
224
GstElement *source_elem;
225
GstElement *decoder_elem;
226
GstElement *encoder_elem;
227
GstElement *sink_elem;
228
GstElement *conv_elem;
229
GstElement *resample_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 sinkbin 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
resample_elem = gst_element_factory_make("audioresample", "audioresample");
269
if(resample_elem == NULL) {
270
gst_transcoder_raise_error(transcoder, _("Could not create audioresample plugin"), NULL);
274
encoder_elem = gst_transcoder_build_encoder(encoder_pipeline);
275
if(encoder_elem == NULL) {
276
gst_transcoder_raise_error(transcoder, _("Could not create encoding pipeline"), encoder_pipeline);
280
encoder_pad = gst_element_get_pad(conv_elem, "sink");
281
if(encoder_pad == NULL) {
282
gst_transcoder_raise_error(transcoder, _("Could not get sink pad from encoder"), NULL);
286
gst_bin_add_many(GST_BIN(transcoder->sink_bin), conv_elem, resample_elem, encoder_elem, sink_elem, NULL);
287
gst_element_link_many(conv_elem, resample_elem, encoder_elem, sink_elem, NULL);
289
gst_element_add_pad(transcoder->sink_bin, gst_ghost_pad_new("sink", encoder_pad));
290
gst_object_unref(encoder_pad);
292
gst_bin_add_many(GST_BIN(transcoder->pipeline), source_elem, decoder_elem,
293
transcoder->sink_bin, NULL);
295
gst_element_link(source_elem, decoder_elem);
297
g_signal_connect(decoder_elem, "new-decoded-pad",
298
G_CALLBACK(gst_transcoder_new_decoded_pad), transcoder);
300
gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(transcoder->pipeline)),
301
gst_transcoder_bus_callback, transcoder);
309
gst_transcoder_new ()
311
return g_new0 (GstTranscoder, 1);
315
gst_transcoder_free(GstTranscoder *transcoder)
317
g_return_if_fail(transcoder != NULL);
318
gst_transcoder_stop_iterate_timeout(transcoder);
320
if(GST_IS_ELEMENT(transcoder->pipeline)) {
321
gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_NULL);
322
gst_object_unref(GST_OBJECT(transcoder->pipeline));
325
if(transcoder->output_uri != NULL) {
326
g_free(transcoder->output_uri);
327
transcoder->output_uri = NULL;
335
gst_transcoder_transcode(GstTranscoder *transcoder, const gchar *input_uri,
336
const gchar *output_uri, const gchar *encoder_pipeline)
338
g_return_if_fail(transcoder != NULL);
340
if(transcoder->is_transcoding) {
344
if(!gst_transcoder_create_pipeline(transcoder, input_uri, output_uri, encoder_pipeline)) {
345
gst_transcoder_raise_error(transcoder, _("Could not construct pipeline"), NULL);
349
if(transcoder->output_uri != NULL) {
350
g_free(transcoder->output_uri);
353
transcoder->output_uri = g_strdup(output_uri);
354
transcoder->is_transcoding = TRUE;
356
gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_PLAYING);
357
gst_transcoder_start_iterate_timeout(transcoder);
361
gst_transcoder_cancel(GstTranscoder *transcoder)
363
g_return_if_fail(transcoder != NULL);
364
gst_transcoder_stop_iterate_timeout(transcoder);
366
transcoder->is_transcoding = FALSE;
368
if(GST_IS_ELEMENT(transcoder->pipeline)) {
369
gst_element_set_state(GST_ELEMENT(transcoder->pipeline), GST_STATE_NULL);
370
gst_object_unref(GST_OBJECT(transcoder->pipeline));
373
g_remove(transcoder->output_uri);
377
gst_transcoder_set_progress_callback(GstTranscoder *transcoder,
378
GstTranscoderProgressCallback cb)
380
g_return_if_fail(transcoder != NULL);
381
transcoder->progress_cb = cb;
385
gst_transcoder_set_finished_callback(GstTranscoder *transcoder,
386
GstTranscoderFinishedCallback cb)
388
g_return_if_fail(transcoder != NULL);
389
transcoder->finished_cb = cb;
393
gst_transcoder_set_error_callback(GstTranscoder *transcoder,
394
GstTranscoderErrorCallback cb)
396
g_return_if_fail(transcoder != NULL);
397
transcoder->error_cb = cb;
401
gst_transcoder_get_is_transcoding(GstTranscoder *transcoder)
403
g_return_val_if_fail(transcoder != NULL, FALSE);
404
return transcoder->is_transcoding;