#include <gst/gst.h>
#include <string.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
/* these are the caps we are going to pass through the appsink and appsrc */
const gchar *audio_caps =
"audio/x-raw,format=S16LE,channels=2,rate=44100,layout=interleaved";
#define DEVICE "alsa_output.pci-0000_00_05.0.analog-stereo.monitor"
#define APPSRC_PULL_MODE (0) /* appsrc working in pull mode or not */
#define PLAY_FILE (0) /* play file or get data from pulseaudio */
#define SAVE_FILE (1) /* save a file or playing directly */
static GstBuffer* s_app_buffer = NULL;
typedef struct
{
GMainLoop *loop;
GstElement *sink_pipeline;
GstElement *src_pipeline;
} ProgramData;
/* called when the appsink notifies us that there is a new buffer ready for
* processing */
static GstFlowReturn
on_new_sample_from_sink (GstElement * sink_elm, ProgramData * data)
{
GstSample *sample = NULL;
GstBuffer *buffer, *app_buffer;
GstElement *sink, *source;
GstFlowReturn ret;
GstMapInfo map;
/* get the sample from appsink */
//sample = gst_app_sink_pull_sample (GST_APP_SINK (sink_elm));
#if 0
sink = gst_bin_get_by_name (GST_BIN (data->src_pipeline), "testsink");
g_signal_emit_by_name (sink, "pull-sample", &sample, &ret);
gst_object_unref (sink);
#else
g_signal_emit_by_name(sink_elm, "pull-sample", &sample, &ret);
#endif
if(sample){
buffer = gst_sample_get_buffer (sample);
g_print("on_new_sample_from_sink() call!; size = %d\n", gst_buffer_get_size(buffer));
}
else{
g_print("sample is NULL \n");
return ret;
}
/* 查看pull 到的sample 数据 */
#if 0
/* Mapping a buffer can fail (non-readable) */
if (gst_buffer_map (buffer, &map, GST_MAP_READ)) {
/* print the buffer data for debug */
int i = 0, j = 0;
for(; i < 10; i++)
g_print("%x ", map.data[i]);
g_print("\n");
}
#endif
/* make a copy */
#if !APPSRC_PULL_MODE
app_buffer = gst_buffer_copy (buffer);
#else
s_app_buffer = gst_buffer_copy(buffer);
#endif
/* we don't need the appsink sample anymore */
gst_sample_unref (sample);
/* 如果appsrc 为push 模式,直接将数据注入给appsrc */
/* get source an push new buffer */
#if !APPSRC_PULL_MODE
source = gst_bin_get_by_name (GST_BIN (data->sink_pipeline), "testsource");
//ret = gst_app_src_push_buffer (GST_APP_SRC (source), app_buffer);
g_signal_emit_by_name( source, "push-buffer", app_buffer, &ret );//数据送入pipeline
gst_object_unref (source);
gst_buffer_unref(app_buffer);
#endif
return ret;
}
/* called when we get a GstMessage from the source pipeline when we get EOS, we
* notify the appsrc of it. */
static gboolean
on_appsink_message (GstBus * bus, GstMessage * message, ProgramData * data)
{
GstElement *source;
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
g_print ("The source got dry\n");
source = gst_bin_get_by_name (GST_BIN (data->sink_pipeline), "testsource");
//gst_app_src_end_of_stream (GST_APP_SRC (source));
g_signal_emit_by_name (source, "end-of-stream", NULL);
gst_object_unref (source);
break;
case GST_MESSAGE_ERROR:
g_print ("Received error\n");
g_main_loop_quit (data->loop);
break;
default:
break;
}
return TRUE;
}
/* called when we get a GstMessage from the sink pipeline when we get EOS, we
* exit the mainloop and this testapp. */
static gboolean
on_appsrc_message (GstBus * bus, GstMessage * message, ProgramData * data)
{
/* nil */
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
g_print ("Finished playback\n");
g_main_loop_quit (data->loop);
break;
case GST_MESSAGE_ERROR:
g_print ("Received error\n");
g_main_loop_quit (data->loop);
break;
default:
break;
}
return TRUE;
}
static gboolean read_data(ProgramData* app_data)
{
GstElement *appsink;
appsink = gst_bin_get_by_name (GST_BIN (app_data->src_pipeline), "testsink");
GstFlowReturn ret = on_new_sample_from_sink(appsink, app_data);
gst_object_unref(appsink);
GstElement *source = NULL;
if(s_app_buffer){
g_print("read data()....\n");
source = gst_bin_get_by_name (GST_BIN (app_data->sink_pipeline), "testsource");
//ret = gst_app_src_push_buffer (GST_APP_SRC (source), app_buffer);
g_signal_emit_by_name( source, "push-buffer", s_app_buffer, &ret );//数据送入pipeline
gst_object_unref (source);
gst_buffer_unref(s_app_buffer);
s_app_buffer = NULL;
}
else{
g_print("read_data() s_app_buffer is NULL\n");
}
return TRUE;
}
static void on_appsrc_need_data(GstElement *appsrc,
guint unused_size,
ProgramData* app_data)
{
g_print("on_appsrc_need_data() call !!!\n");
guint src_id = g_idle_add ((GSourceFunc) read_data, app_data);
return;
}
int main (int argc, char *argv[])
{
gchar *filename = NULL;
ProgramData *data = NULL;
gchar *string = NULL;
GstBus *bus = NULL;
GstElement *testsink = NULL;
GstElement *testsource = NULL;
gst_init (&argc, &argv);
if (argc == 2)
filename = g_strdup (argv[1]);
else
filename = g_strdup ("xxx/xxxx.wav");
if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
g_print ("File %s does not exist\n", filename);
return -1;
}
data = g_new0 (ProgramData, 1);
data->loop = g_main_loop_new (NULL, FALSE);
/* setting up source pipeline, we read from a file and convert to our desired
* caps. */
#if PLAY_FILE /* 播放wav 歌曲 */
string =
g_strdup_printf
("filesrc location=\"%s\" ! wavparse ! audioconvert ! appsink caps=\"%s\" name=testsink",
filename, audio_caps);
#else /* 从pulseaudio 抓取 */
string =
g_strdup_printf
("pulsesrc device=%s ! audioconvert ! appsink caps=\"%s\" name=testsink",
DEVICE, audio_caps);
#endif
g_free (filename);
g_print("%s\n", string);
data->src_pipeline = gst_parse_launch (string, NULL);
g_free (string);
if (data->src_pipeline == NULL) {
g_print ("Bad source\n");
return -1;
}
/* to be notified of messages from this pipeline, mostly EOS */
bus = gst_element_get_bus (data->src_pipeline);
gst_bus_add_watch (bus, (GstBusFunc) on_appsink_message, data);
gst_object_unref (bus);
/* we use appsink in push mode, it sends us a signal when data is available
* and we pull out the data in the signal callback. We want the appsink to
* push as fast as it can, hence the sync=false */
testsink = gst_bin_get_by_name (GST_BIN (data->src_pipeline), "testsink");
#if !APPSRC_PULL_MODE
g_object_set (G_OBJECT (testsink), "emit-signals", TRUE, "sync", FALSE, NULL);
g_signal_connect (testsink, "new-sample",
G_CALLBACK (on_new_sample_from_sink), data);
#endif
gst_object_unref (testsink);
/* setting up sink pipeline, we push audio data into this pipeline that will
* then play it back using the default audio sink. We have no blocking
* behaviour on the src which means that we will push the entire file into
* memory. */
#if !SAVE_FILE
string =
g_strdup_printf ("appsrc name=testsource caps=\"%s\" ! autoaudiosink",
audio_caps);
#else
string =
g_strdup_printf ("appsrc name=testsource caps=\"%s\" ! avenc_aac ! avmux_adts ! filesink location=sink_src.aac",
audio_caps);
#endif
data->sink_pipeline = gst_parse_launch (string, NULL);
g_free (string);
if (data->sink_pipeline == NULL) {
g_print ("Bad sink\n");
return -1;
}
testsource = gst_bin_get_by_name (GST_BIN (data->sink_pipeline), "testsource");
/* configure for time-based format */
g_object_set (testsource, "format", GST_FORMAT_TIME, NULL);
/* uncomment the next line to block when appsrc has buffered enough */
/* g_object_set (testsource, "block", TRUE, NULL); */
#if APPSRC_PULL_MODE
g_signal_connect (testsource, "need-data",
G_CALLBACK (on_appsrc_need_data), data);
#endif
gst_object_unref (testsource);
bus = gst_element_get_bus (data->sink_pipeline);
gst_bus_add_watch (bus, (GstBusFunc) on_appsrc_message, data);
gst_object_unref (bus);
/* launching things */
gst_element_set_state (data->src_pipeline, GST_STATE_PLAYING);
gst_element_set_state (data->sink_pipeline, GST_STATE_PLAYING);
/* let's run !, this loop will quit when the sink pipeline goes EOS or when an
* error occurs in the source or sink pipelines. */
g_print ("Let's run!\n");
g_main_loop_run (data->loop);
g_print ("Going out\n");
gst_element_set_state (data->src_pipeline, GST_STATE_NULL);
gst_element_set_state (data->sink_pipeline, GST_STATE_NULL);
gst_object_unref (data->src_pipeline);
gst_object_unref (data->sink_pipeline);
g_main_loop_unref (data->loop);
g_free (data);
return 0;
}