Problems to rip CD using cdparanoiasrc

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Problems to rip CD using cdparanoiasrc

Pascal Ognibene-3
Hi all,

I'm trying to rip a CD using a simple gstreamer pipeline - something that is already done in several programs,
but i'd like to understand myself how it works.

In the code below, I basically use the EOS event to seek to the next cd track.
But the behavior I see is weird:
-the first track is ripped ok
-I call the start_track_rip function for track 2
-the program blocks here forever.

I must be doing something really wrong but I can't see what, even by the looking at the sources of sound juicer for example.
Any help is welcome,

- pog

Code below:
-----------------

#include <glib.h>
#include <gst/gst.h>
#include <string.h>

guint current_rip_track = 1;
guint max_rip_track = 10;

void start_track_rip();

static GMainLoop *loop;
GstElement *pipeline;

static gboolean
my_bus_callback(GstBus * bus, GstMessage * message, gpointer data)
{
    g_print("Got %s message\n", GST_MESSAGE_TYPE_NAME(message));
    switch (GST_MESSAGE_TYPE(message)) {
    case GST_MESSAGE_ERROR:{
            GError *err;
            gchar *debug;
            gst_message_parse_error(message, &err, &debug);
            g_print("Error: %s\n", err->message);
            g_error_free(err);
            g_free(debug);
            g_main_loop_quit(loop);
            break;
        }
    case GST_MESSAGE_EOS:
        if (current_rip_track < max_rip_track) {
            current_rip_track++;
            g_print("going to next track %d\n", current_rip_track);
            start_track_rip();
        } else {
            g_print("no more tracks, stopping\n");
            return FALSE;
        }
        break;
    default:
        /* unhandled message */
        break;
    }
    return TRUE;
}

static gboolean cb_print_position(GstElement * pipeline)
{
    GstState state, pending_state;
    static GstFormat format = GST_FORMAT_TIME;
    gint64 pos, len;

    gst_element_get_state(pipeline, &state, &pending_state, 0);
    if (state != GST_STATE_PLAYING && pending_state != GST_STATE_PLAYING) {
        return FALSE;
    }

    if (gst_element_query_position(pipeline, &format, &pos)
        && gst_element_query_duration(pipeline, &format, &len)) {
        g_print("Time: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "\r",
            GST_TIME_ARGS(pos), GST_TIME_ARGS(len));
    }
    /* call me again */
    return TRUE;
}

int main()
{
    gst_init(NULL, NULL);

    GstElement *source, *filter, *sink;
    GstBus *bus;

    // build pipeline
    pipeline = gst_pipeline_new("rip-pipeline");
    source = gst_element_factory_make("cdparanoiasrc", "mycdparanoia");
    filter = gst_element_factory_make("lame", "encoder");
    sink = gst_element_factory_make("filesink", "myfilesink");
    gst_bin_add_many(GST_BIN(pipeline), source, filter, sink, NULL);

    if (!gst_element_link_many(source, filter, sink, NULL)) {
        g_warning("Failed to link pipeline elements!");
    }

    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
    gst_bus_add_watch(bus, my_bus_callback, NULL);
    gst_object_unref(bus);

    g_object_set(G_OBJECT(source), "device", "/dev/hda", NULL);

    start_track_rip();

    g_print("starting main loop\n");

    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    /* clean up */
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    g_main_loop_unref(loop);

    return (0);
}

void start_track_rip()
{
    gchar buf[2048];

    GstElement *sink = gst_bin_get_by_name(GST_BIN(pipeline), "myfilesink");
    strcpy(buf, "/tmp/music_");
    gchar buf2[10];
    sprintf(buf2, "%02d", current_rip_track);
    strcat(buf, buf2);
    strcat(buf, ".mp3");
    gst_element_set_state(sink, GST_STATE_NULL);
    g_object_set(G_OBJECT(sink), "location", buf, NULL);

    // seek to track
    GstElement *cd = gst_bin_get_by_name(GST_BIN(pipeline), "mycdparanoia");
    g_object_set(G_OBJECT(cd), "track", current_rip_track, NULL);
    GstStateChangeReturn state_ret =
        gst_element_set_state(pipeline, GST_STATE_PLAYING);

    if (state_ret == GST_STATE_CHANGE_FAILURE) {
        g_print("01 state change failed\n");
    } else if (state_ret == GST_STATE_CHANGE_SUCCESS) {
        g_print("02 state change successful\n");
    } else if (state_ret == GST_STATE_CHANGE_ASYNC) {
        // what am I supposed to do here?
        g_print("seek async\n");
        state_ret =
            gst_element_get_state(pipeline, NULL, NULL, GST_SECOND / 2);
    }

    g_timeout_add_seconds(1, (GSourceFunc) cb_print_position, pipeline);
}

------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gstreamer-devel
Reply | Threaded
Open this post in threaded view
|

Re: [gst-devel] Problems to rip CD using cdparanoiasrc

Tim-Philipp Müller-2
On Fri, 2010-01-22 at 21:27 +0100, Pascal Ognibene wrote:

Hi,

> I'm trying to rip a CD using a simple gstreamer pipeline (..)
> In the code below, I basically use the EOS event to seek to the next
> cd track.
> But the behavior I see is weird:
> -the first track is ripped ok
> -I call the start_track_rip function for track 2
> -the program blocks here forever.
>
> I must be doing something really wrong but I can't see what, even by
> the looking at the sources of sound juicer for example.
> Any help is welcome,

Not related to your problem at all, but you might want to add a queue
after cdparanoiasrc to make sure encoding continues if there are
problems reading the CD (dirt on the disc etc.).

I haven't actually run your code, but it looks to me like you get an EOS
and then change the track number via the property and that's it.
However, when you get the EOS, streaming has already stopped,
cdparanoiasrc is not running any longer. At that point, setting the
property will not make it start up again.

You have two options here:

a) set pipeline state back to READY (or NULL), then change to PLAYING
again.

b) issue a seek in "track" format (track number might be 0-based in this
case, don't remember exactly) to make it start up again.

(b) might work in this particular case where you're just writing mp3
data to a file, but (a) is likely to work better in general, esp. with
other encoders, if you add muxers or if you add a tag writer.

So in short, try changing

  gst_element_set_state(sink, GST_STATE_NULL);

at the beginning of start_track_rip() to

  gst_element_set_state(pipeline, GST_STATE_NULL);

Cheers
 -Tim



------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gstreamer-devel
Reply | Threaded
Open this post in threaded view
|

Re: [gst-devel] Problems to rip CD using cdparanoiasrc

Pascal Ognibene-3


2010/1/23 Tim-Philipp Müller <[hidden email]>
On Fri, 2010-01-22 at 21:27 +0100, Pascal Ognibene wrote:

Hi,

> I'm trying to rip a CD using a simple gstreamer pipeline (..)
> In the code below, I basically use the EOS event to seek to the next
> cd track.
> But the behavior I see is weird:
> -the first track is ripped ok
> -I call the start_track_rip function for track 2
> -the program blocks here forever.
>
> I must be doing something really wrong but I can't see what, even by
> the looking at the sources of sound juicer for example.
> Any help is welcome,

Not related to your problem at all, but you might want to add a queue
after cdparanoiasrc to make sure encoding continues if there are
problems reading the CD (dirt on the disc etc.).

I haven't actually run your code, but it looks to me like you get an EOS
and then change the track number via the property and that's it.
However, when you get the EOS, streaming has already stopped,
cdparanoiasrc is not running any longer. At that point, setting the
property will not make it start up again.

You have two options here:

a) set pipeline state back to READY (or NULL), then change to PLAYING
again.

b) issue a seek in "track" format (track number might be 0-based in this
case, don't remember exactly) to make it start up again.

(b) might work in this particular case where you're just writing mp3
data to a file, but (a) is likely to work better in general, esp. with
other encoders, if you add muxers or if you add a tag writer.

So in short, try changing

 gst_element_set_state(sink, GST_STATE_NULL);

at the beginning of start_track_rip() to

 gst_element_set_state(pipeline, GST_STATE_NULL);


Thank you for your help!

Actually, I stepped through the Sound Juicer code using gdb, and discovered that they destroy and recreate the pipeline for each track they rip.
I've done the same, and it works fine. I'll try your solution as well to see if i can get faster seek operations between tracks.
Oh, and I'm going to add a queue in the middle as per your excellent suggestion - I've been testing with a brand new CD so did not meet rip glitches yet,
but this will happen for sure in "real life".

Cheers,

-pog

------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/gstreamer-devel