#include #include #include #include /************************************************************************/ #include static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ( "src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS( "audio/x-raw-int, " "width = (int) 16, " "depth = (int) 16, " "endianness = (int) BYTE_ORDER, " "channels = (int) 1, " "rate = (int) 8000, " "signed = (boolean) true")); typedef struct _AstAudioSrc AstAudioSrc; typedef struct _AstAudioSrcClass AstAudioSrcClass; /** * AstAudioSrc: * * Opaque #AstAudioSrc data structure */ struct _AstAudioSrc { GstElement element; GstPad *srcpad; gboolean push; gboolean playing; }; struct _AstAudioSrcClass { GstElementClass parent_class; }; /* Standard macros for defining types for this element. */ #define GST_TYPE_AST_AUDIO_SRC \ (ast_audio_src_get_type()) #define AST_AUDIO_SRC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AST_AUDIO_SRC,AstAudioSrc)) #define AST_AUDIO_SRC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AST_AUDIO_SRC,AstAudioSrcClass)) #define GST_IS_AST_AUDIO_SRC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AST_AUDIO_SRC)) #define GST_IS_AST_AUDIO_SRC_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AST_AUDIO_SRC)) /* Standard function returning type information. */ GType ast_audio_src_get_type(void); GST_BOILERPLATE(AstAudioSrc, ast_audio_src, GstElement, GST_TYPE_ELEMENT); static GstElementDetails ast_audio_src_details = GST_ELEMENT_DETAILS("Asterisk Channel Audio", "Blah", "Blah Blah", "Jeffrey C. Ollie "); static void ast_audio_src_base_init (gpointer g_class) { GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); gst_element_class_add_pad_template(gstelement_class, gst_static_pad_template_get(&srctemplate)); gst_element_class_set_details(gstelement_class, &ast_audio_src_details); } static GstStateChangeReturn ast_audio_src_change_state (GstElement *element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; AstAudioSrc *aasrc = AST_AUDIO_SRC(element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: fprintf(stderr, "AstAudioSrc transitioning to READY...\n"); break; case GST_STATE_CHANGE_READY_TO_PAUSED: fprintf(stderr, "AstAudioSrc transitioning to PAUSED...\n"); fprintf(stderr, "Sending new segment event...\n"); if (!gst_pad_push_event(aasrc->srcpad, gst_event_new_new_segment(FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0))) { fprintf(stderr, "Unable to send new segment event...\n"); } break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: fprintf(stderr, "AstAudioSrc transitioning to PLAYING...\n"); aasrc->playing = TRUE; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: fprintf(stderr, "AstAudioSrc transitioning to PAUSED...\n"); aasrc->playing = FALSE; break; case GST_STATE_CHANGE_PAUSED_TO_READY: fprintf(stderr, "AstAudioSrc transitioning to PAUSED...\n"); break; case GST_STATE_CHANGE_READY_TO_NULL: fprintf(stderr, "AstAudioSrc transitioning to NULL...\n"); break; default: break; } if (ret == GST_STATE_CHANGE_SUCCESS && GST_STATE_CHANGE_READY_TO_PAUSED) { ret = GST_STATE_CHANGE_NO_PREROLL; } return ret; } static void ast_audio_src_class_init(AstAudioSrcClass *klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = G_OBJECT_CLASS (klass); gstelement_class = GST_ELEMENT_CLASS (klass); gstelement_class->change_state = ast_audio_src_change_state; } static gboolean ast_audio_src_activatepush(GstPad *pad, gboolean active) { GstElement *e; AstAudioSrc *a; if (active) fprintf(stderr, "Activating push on pad...\n"); else fprintf(stderr, "Deactivating push on pad...\n"); e = gst_pad_get_parent_element(pad); a = AST_AUDIO_SRC(e); a->push = active; return TRUE; } static void ast_audio_src_init (AstAudioSrc *aasrc, AstAudioSrcClass *klass) { aasrc->srcpad = gst_pad_new_from_template(gst_element_class_get_pad_template(GST_ELEMENT_CLASS(klass), "src"), "src"); aasrc->push = FALSE; aasrc->playing = FALSE; gst_pad_set_activatepush_function(aasrc->srcpad, ast_audio_src_activatepush); gst_element_add_pad(GST_ELEMENT(aasrc), aasrc->srcpad); } static gboolean register_elements (GstPlugin *plugin) { return gst_element_register(plugin, "ast_audio_src", GST_RANK_NONE, GST_TYPE_AST_AUDIO_SRC); } static GstPluginDesc plugin_desc = { GST_VERSION_MAJOR, GST_VERSION_MINOR, "format_gstreamer_plugins", "Private elements of format_gstreamer", register_elements, "0.0.1", "GPL", "format_gstreamer", "format_gstreamer", "http://www.asterisk.org/", GST_PADDING_INIT}; /************************************************************************/ struct filestream { FILE *f; GstElement *pipeline; GstElement *audio; guint64 offset; GstClockTime timestamp; }; static void *write_runner(void *data) { struct filestream *fs; GstBus *bus; GstMessage *message; fprintf(stderr, "Write runner started...\n"); fs = (struct filestream *) data; bus = gst_pipeline_get_bus(GST_PIPELINE(fs->pipeline)); while(1) { message = gst_bus_poll(bus, GST_MESSAGE_EOS, -1); switch(GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_EOS: { fprintf(stderr, "EOS message received, shutting down...\n"); /* shut down pipeline here */ gst_element_set_state(fs->pipeline, GST_STATE_NULL); fprintf(stderr, "Pipeline state set to NULL.\n"); fprintf(stderr, "Unreferencing pipeline...\n"); gst_object_unref(GST_OBJECT(fs->pipeline)); fprintf(stderr, "Unreferencing pipeline... finished\n"); fprintf(stderr, "Closing file...\n"); fclose(fs->f); fprintf(stderr, "Freeing filestream...\n"); free(fs); fprintf(stderr, "Freeing filestream... finished.\n"); fs = NULL; return NULL; break; } default: { break; } } } fprintf(stderr, "Write runner finished...\n"); return NULL; } static struct filestream *gstreamer_rewrite(FILE *f) { struct filestream *tmp; static pthread_t runner; GstElement *fdsink; GstElement *subpipeline; GError *error; GstElement *audio_input; GstElement *video_input; GstElement *output; fprintf(stderr, "Opening new stream...\n"); if ((tmp = malloc(sizeof(struct filestream)))) { memset(tmp, 0, sizeof(struct filestream)); tmp->f = f; fprintf(stderr, "Creating pipeline...\n"); tmp->pipeline = gst_pipeline_new("format_gstreamer"); if (!tmp->pipeline) { fprintf(stderr, "Unable to create pipeline...\n"); free(tmp); return NULL; } fprintf(stderr, "Creating ast_audio_src...\n"); tmp->audio = gst_element_factory_make("ast_audio_src", "ast_audio_src"); if (!tmp->audio) { fprintf(stderr, "Could not make ast_audio_src element!\n"); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } gst_bin_add(GST_BIN(tmp->pipeline), tmp->audio); fprintf(stderr, "Creating fdsink fd=%i...\n", fileno(tmp->f)); fdsink = gst_element_factory_make("fdsink", "fdsink"); if (!fdsink) { fprintf(stderr, "Unable to link queue and audioconvert!\n"); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } g_object_set(G_OBJECT(fdsink), "fd", fileno(tmp->f), NULL); gst_bin_add(GST_BIN(tmp->pipeline), fdsink); fprintf(stderr, "Creating sub-pipeline...\n"); error = NULL; subpipeline = gst_parse_launch("queue name=audio_input ! audioconvert ! vorbisenc ! oggmux name=output", &error); if (error) { fprintf(stderr, "There was an error creating the sub pipeline: %s\n", error->message); if (subpipeline) gst_object_unref(subpipeline); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } gst_bin_add(GST_BIN(tmp->pipeline), subpipeline); audio_input = gst_bin_get_by_name(GST_BIN(subpipeline), "audio_input"); if (!audio_input) { fprintf(stderr, "Unable to find audio input element in pipeline!\n"); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } if (!gst_element_link(tmp->audio, audio_input)) { fprintf(stderr, "Unable to link audio input...\n"); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } output = gst_bin_get_by_name(GST_BIN(subpipeline), "output"); if (!output) { fprintf(stderr, "Unable to find output element in pipeline!\n"); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } if (!gst_element_link(output, fdsink)) { fprintf(stderr, "Unable to link output...\n"); gst_object_unref(tmp->pipeline); free(tmp); return NULL; } fprintf(stderr, "Setting pipeline to playing state...\n"); gst_element_set_state(tmp->pipeline, GST_STATE_PLAYING); fprintf(stderr, "Starting thread...\n"); pthread_create(&runner, NULL, write_runner, tmp); } else { fprintf(stderr, "Out of memory...\n"); } return tmp; } static void gstreamer_close(struct filestream *fs) { AstAudioSrc *a; a = AST_AUDIO_SRC(fs->audio); fprintf(stderr, "Close received, sending EOS.\n"); gst_pad_push_event(a->srcpad, gst_event_new_eos()); } static int gstreamer_write(struct filestream *fs) { GstBuffer *buffer; GstClockTime duration; AstAudioSrc *a; a = AST_AUDIO_SRC(fs->audio); if (!(a->push)) { fprintf(stderr, "Dropping frame because pad is not ready to push.\n"); return 0; } if (!(a->playing)) { fprintf(stderr, "Dropping frame beause not playing yet...\n"); return 0; } duration = 160 * (1000000000 / 8000); /*fprintf(stderr, "Creating buffer for frame (%i samples, duration %ld) ...\n", f->samples, (long int) duration);*/ /* create buffer and send it */ buffer = gst_buffer_new_and_alloc(160 *2); gst_buffer_set_caps(buffer, gst_pad_get_caps(a->srcpad)); memset(GST_BUFFER_DATA(buffer), 0, 160 * 2); GST_BUFFER_OFFSET(buffer) = fs->offset; GST_BUFFER_SIZE(buffer) = 160 * 2; GST_BUFFER_TIMESTAMP(buffer) = fs->timestamp; GST_BUFFER_DURATION(buffer) = duration; fs->offset += 160 * 2; fs->timestamp += duration; /*fprintf(stderr, "Pushing frame...\n");*/ gst_pad_push(a->srcpad, buffer); return 0; } int main(int argc, char *argv[]) { struct filestream *fs; FILE *f; int i; fprintf(stderr, "Gstreamer initialization starting...\n"); gst_init(NULL, NULL); fprintf(stderr, "Gstreamer initialization finished...\n"); fprintf(stderr, "Enabling gstreamer debugging...\n"); gst_debug_set_colored(FALSE); gst_debug_set_active(TRUE); gst_debug_set_default_threshold(GST_LEVEL_WARNING); fprintf(stderr, "Registering static plugins...\n"); _gst_plugin_register_static(&plugin_desc); fprintf(stderr, "Registering static plugins... finished\n"); f = fopen("/tmp/t.ogg", "w"); fs = gstreamer_rewrite(f); for (i = 0; i < 100; i++) { usleep(20000); /* 20 ms */ gstreamer_write(fs); } gstreamer_close(fs); usleep(10000000); /* 10 s */ }