简体   繁体   English

如何在Gstreamer中修改音频流的播放速度?

[英]How to modify playback speed of audio stream in Gstreamer?

Hi everyone I am trying to change the playback speed of an audio file using the gstreamer library in c. 大家好,我正在尝试使用c中的gstreamer库更改音频文件的播放速度。 I've followed most of the tutorials on the gstreamer website but the only thing that is not working is the playback speed. 我已经关注了gstreamer网站上的大多数教程,但唯一无法正常工作的是播放速度。

The way it is set up right now, the speed should be doubled when a '.' 现在设置的方式,当“。”时速度应加倍。 is encountered but nothing happens. 遇到但没有任何反应。 Can any experienced gstreamer users provide some insight? 有经验的gstreamer用户可以提供一些见解吗?

typedef struct bindata {
    GMainLoop *loop;
    GstElement *pipeline, *source, *mp3decoder, *volume, *pulseout;
    gboolean playing;
} bindata;

static bindata data;

static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer *misc){
    //GMainLoop *loop = (GMainLoop *) misc;
    switch (GST_MESSAGE_TYPE(msg)){
        case GST_MESSAGE_EOS: {
            g_message("End of stream.\n");
            g_main_loop_quit(data.loop);
            break;
        }
        case GST_MESSAGE_ERROR:{
            GError *error;
            gst_message_parse_error(msg, &error, NULL);
            g_printerr("%s\n", error->message);
            g_error_free(error);

            g_main_loop_quit(data.loop);
            break;
        }
        default: break;
    }
    return TRUE;
}

static gboolean keypress (GIOChannel *src, GIOCondition cond, bindata *data){
    int c;
    gdouble vol;
    GstFormat format = GST_FORMAT_TIME;

    //if(g_io_channel_read_unichar(src, str, NULL) != G_IO_STATUS_NORMAL){
    if((c = getchar()) == EOF ){ 
        return TRUE;
    }

    switch(c){
        case '+':
           g_object_get(data->volume, "volume", &vol,NULL);
           if (vol >= 10) break;
           g_object_set (data->volume, "volume", vol + 0.1, NULL);
           break;
        case '-':
           g_object_get(data->volume, "volume", &vol, NULL);
           if (vol <= 0.1) break;
           g_object_set (data->volume, "volume", vol - 0.1, NULL);
           break;
        case '.':
           g_print("speed up \n");
           gst_element_send_event(data->pulseout, gst_event_new_step(format, 20, 2.0, TRUE, FALSE));
           break;
        case ',':
           g_print("speed down \n");
           break;
        case ' ':
           data->playing = !data->playing;
           gst_element_set_state (data->pipeline, data->playing ? GST_STATE_PLAYING : GST_STATE_PAUSED);
           break;
        default: 
           break;
    }
    return TRUE;
}

int main(int argc, char *argv[]){
    GstBus *bus;
    guint bus_watch_id;
    GIOChannel *io_stdin;

    gst_init(&argc, &argv);
    memset (&data, 0, sizeof(data));
    data.loop = g_main_loop_new(NULL, false);

    if(argc != 2){
       g_printerr("Usage: ./play <URI: mp3 file>");
       return -1;
    }

    io_stdin = g_io_channel_unix_new (fileno (stdin));
    g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) keypress, &data);

    data.pipeline   = gst_pipeline_new ("audio-player");
    data.source     = gst_element_factory_make ("filesrc", "file source");
    data.mp3decoder = gst_element_factory_make ("mad", "mad mp3");
    data.volume     = gst_element_factory_make ("volume", "volume");
    data.pulseout   = gst_element_factory_make ("pulsesink", "pulse audio");

    if(!data.pipeline || !data.source || !data.mp3decoder || !data.pulseout || !data.volume) {
       g_printerr("Some element(s) could not be created. Exiting. \n");
       return -1;
    }

    g_object_set (G_OBJECT (data.source), "location", argv[1], NULL);
    g_object_set (G_OBJECT(data.volume), "volume", 0.01, NULL); 

    bus = gst_pipeline_get_bus(GST_PIPELINE(data.pipeline));
    bus_watch_id = gst_bus_add_watch(bus, bus_call, NULL);
    gst_object_unref(bus);

    gst_bin_add_many(GST_BIN (data.pipeline), data.source, data.mp3decoder, data.volume, data.pulseout, NULL);
    gst_element_link_many (data.source, data.mp3decoder, data.volume, data.pulseout, NULL);

    gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
    data.playing = TRUE;
    g_print ("Running...\n");
    g_main_loop_run(data.loop);
    g_print ("ended. \n");
    gst_element_set_state(data.pipeline, GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(data.pipeline));
    g_source_remove (bus_watch_id);
    g_main_loop_unref (data.loop);

    return 0;
}

通过使用gst_event_new_seek或gst_element_seek通过设置rate属性进行查找来控制播放速度,请参阅https://developer.gnome.org/gstreamer/stable/gstreamer-GstEvent.html#gst-event-new-seek上的文档和http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstElement.html#gst-element-seek

the following, while oriented on video rather than audio,
shows how to change the playback speed.

it is from: 
<http://docs.gstreamer.com/display/GstSDK/Basic+tutorial+13%3A+Playback+speed>

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

typedef struct _CustomData {
  GstElement *pipeline;
  GstElement *video_sink;
  GMainLoop *loop;

  gboolean playing;  /* Playing or Paused */
  gdouble rate;      /* Current playback rate (can be negative) */
} CustomData;

/* Send seek event to change rate */
static void send_seek_event (CustomData *data) {
  gint64 position;
  GstFormat format = GST_FORMAT_TIME;
  GstEvent *seek_event;

  /* Obtain the current position, needed for the seek event */
  if (!gst_element_query_position (data->pipeline, &format, &position)) {
    g_printerr ("Unable to retrieve current position.\n");
    return;
  }

  /* Create the seek event */
  if (data->rate > 0) {
    seek_event = gst_event_new_seek (data->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
        GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_NONE, 0);
  } else {
    seek_event = gst_event_new_seek (data->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
        GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, position);
  }

  if (data->video_sink == NULL) {
    /* If we have not done so, obtain the sink through which we will send the seek events */
    g_object_get (data->pipeline, "video-sink", &data->video_sink, NULL);
  }

  /* Send the event */
  gst_element_send_event (data->video_sink, seek_event);

  g_print ("Current rate: %g\n", data->rate);
}

/* Process keyboard input */
static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data) {
  gchar *str = NULL;

  if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) != G_IO_STATUS_NORMAL) {
    return TRUE;
  }

  switch (g_ascii_tolower (str[0])) {
  case 'p':
    data->playing = !data->playing;
    gst_element_set_state (data->pipeline, data->playing ? GST_STATE_PLAYING : GST_STATE_PAUSED);
    g_print ("Setting state to %s\n", data->playing ? "PLAYING" : "PAUSE");
    break;
  case 's':
    if (g_ascii_isupper (str[0])) {
      data->rate *= 2.0;
    } else {
      data->rate /= 2.0;
    }
    send_seek_event (data);
    break;
  case 'd':
    data->rate *= -1.0;
    send_seek_event (data);
    break;
  case 'n':
    if (data->video_sink == NULL) {
      /* If we have not done so, obtain the sink through which we will send the step events */
      g_object_get (data->pipeline, "video-sink", &data->video_sink, NULL);
    }

    gst_element_send_event (data->video_sink,
        gst_event_new_step (GST_FORMAT_BUFFERS, 1, data->rate, TRUE, FALSE));
    g_print ("Stepping one frame\n");
    break;
  case 'q':
    g_main_loop_quit (data->loop);
    break;
  default:
    break;
  }

  g_free (str);

  return TRUE;
}

int main(int argc, char *argv[]) {
  CustomData data;
  GstStateChangeReturn ret;
  GIOChannel *io_stdin;

  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Initialize our data structure */
  memset (&data, 0, sizeof (data));

  /* Print usage map */
  g_print (
    "USAGE: Choose one of the following options, then press enter:\n"
    " 'P' to toggle between PAUSE and PLAY\n"
    " 'S' to increase playback speed, 's' to decrease playback speed\n"
    " 'D' to toggle playback direction\n"
    " 'N' to move to next frame (in the current direction, better in PAUSE)\n"
    " 'Q' to quit\n");

  /* Build the pipeline */
  data.pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL);

  /* Add a keyboard watch so we get notified of keystrokes */
#ifdef _WIN32
  io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
#else
  io_stdin = g_io_channel_unix_new (fileno (stdin));
#endif
  g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);

  /* Start playing */
  ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state.\n");
    gst_object_unref (data.pipeline);
    return -1;
  }
  data.playing = TRUE;
  data.rate = 1.0;

  /* Create a GLib Main Loop and set it to run */
  data.loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (data.loop);

  /* Free resources */
  g_main_loop_unref (data.loop);
  g_io_channel_unref (io_stdin);
  gst_element_set_state (data.pipeline, GST_STATE_NULL);
  if (data.video_sink != NULL)
    gst_object_unref (data.video_sink);
  gst_object_unref (data.pipeline);
  return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM