[mistelix] Audio support for Vorbis generated videos
- From: Jordi Mas <jmas src gnome org>
- To: svn-commits-list gnome org
- Subject: [mistelix] Audio support for Vorbis generated videos
- Date: Tue, 5 May 2009 14:31:23 -0400 (EDT)
commit 7da871b5954737e5ed481d909148653707d0157b
Author: Jordi Mas <jmas softcatala org>
Date: Tue May 5 20:27:56 2009 +0200
Audio support for Vorbis generated videos
---
libmistelix/Makefile.am | 1 +
libmistelix/mistelix.c | 372 +++++++++++++++++++++++++++++++----
libmistelix/mistelix.h | 2 +
src/core/Dependencies.cs | 14 +-
src/dialogs/AudioSelectionDialog.cs | 6 +
src/mistelix.glade | 1 +
src/widgets/BrowseFile.cs | 20 ++-
7 files changed, 375 insertions(+), 41 deletions(-)
diff --git a/libmistelix/Makefile.am b/libmistelix/Makefile.am
index ee4b820..55825eb 100644
--- a/libmistelix/Makefile.am
+++ b/libmistelix/Makefile.am
@@ -13,6 +13,7 @@ libmistelix_la_SOURCES = \
noinst_HEADERS = \
mistelix.h
+libmistelix_la_CFLAGS = $(LIBMISTELIX_CFLAGS) -Wall -Wno-unused -Wno-format
libmistelix_la_LDFLAGS = -export-dynamic -module -avoid-version
libmistelix_la_LIBADD = $(LIBMISTELIX_LIBS)
diff --git a/libmistelix/mistelix.c b/libmistelix/mistelix.c
index e45625c..9b974bf 100644
--- a/libmistelix/mistelix.c
+++ b/libmistelix/mistelix.c
@@ -23,13 +23,20 @@
#include <stdio.h>
#include <netdb.h>
+#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <gst/gst.h>
+#include <sys/wait.h>
#include "mistelix.h"
+#define SEEK_TIMEOUT 5 * GST_MSECOND
+
+/* Enable this define for debugging */
+
//#define _DEBUG 1
typedef struct
@@ -240,7 +247,7 @@ void mistelix_launchtool (const char* app, const char* args, const char* in_file
// Parent process returns once the child has completed
if (pid > 0) {
- wait ();
+ wait (NULL);
return;
}
@@ -517,10 +524,10 @@ cb_typefound (GstElement *typefind,
g_free (type);
}
-//
-// Do not want to start the thread until is necessary
-// to allow for example to add an audio channel before launching it
-//
+/*
+ Do not want to start the thread until is necessary
+ to allow for example to add an audio channel before launching it
+*/
void
mistelix_check_started ()
{
@@ -535,52 +542,338 @@ mistelix_check_started ()
mistelix_socket_connect ();
}
+/*
+ Sends a seek event to a pad
+*/
+void
+send_seek_event (GstElement* pipeline, GstPad* pad, gboolean flush)
+{
+ gboolean res = FALSE;
+ GstEvent *event;
+ GstSeekFlags flags;
+
+ flags = GST_SEEK_FLAG_SEGMENT;
+
+ if (flush)
+ flags |= GST_SEEK_FLAG_FLUSH;
+
+ /* Seek from the begining */
+ event = gst_event_new_seek (1, GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, -1);
+ res = gst_pad_send_event (pad, event);
+ if (res)
+ gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
+ else
+ printf ("send_seek_event: error sending seek event\n");
+}
+
+/*
+ Monitor the EOS event for the sink pad
+ and resend it to the pipeline to finish it
+*/
+static gboolean
+gst_handle_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstElement* element = (GstElement *) gst_object_get_parent (GST_OBJECT (pad));
+
+#ifdef _DEBUG
+ printf ("--->* gst_handle_sink_event %s %s -> %s (%u)\n",
+ gst_element_get_name (element),
+ gst_element_get_name (pad),
+ gst_event_type_get_name (event->type),
+ event->type);
+#endif
+ if (event->type == GST_EVENT_EOS) {
+ GstElement* pipe = (GstElement *) gst_object_get_parent (GST_OBJECT (element));
+ GstBus *bus = gst_element_get_bus (pipe);
+ gst_bus_post (bus, gst_message_new_eos (GST_OBJECT (pipe)));
+#ifdef _DEBUG
+ printf ("Posted EOS\n");
+#endif
+ }
+ return gst_pad_event_default (pad, event);
+}
+
+#ifdef _DEBUG
+
+/*
+ Lists all the elements in a pipeline
+*/
+void
+listelements (GstBin* bin)
+{
+ gpointer point, point_pad;
+ GstIterator *it, *it_pads;
+
+ it = gst_bin_iterate_elements (bin);
+
+ printf ("----\n");
+ while (gst_iterator_next (it, &point) == GST_ITERATOR_OK) {
+ GstElement* element = GST_ELEMENT (point);
+
+ it_pads = gst_element_iterate_pads (element);
+ printf ("element -> %s\n", gst_element_get_name (element));
+
+ while (gst_iterator_next (it_pads, &point_pad) == GST_ITERATOR_OK) {
+ GstPad * pad = GST_PAD (point_pad);
+ printf ("pad-> %s\n", gst_element_get_name (pad));
+ }
+ }
+ printf ("----\n");
+}
+#endif
+
+gboolean
+mistelix_is_codec (const char* name)
+{
+ int ncodecs, i;
+ gboolean found = FALSE;
+
+ ncodecs = mistelix_get_codecs_count ();
+
+ char *codecs[ncodecs];
+ mistelix_get_codecs (codecs);
-// Method to launch gstreamer thread. Method signature as required by g_thread_create
+ for (i = 0; i < ncodecs; i++ )
+ {
+ if (strcmp (name, codecs[i]) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ for (i = 0; i < ncodecs; i++)
+ free (codecs [i]);
+
+#ifdef _DEBUG
+ printf ("Codec %s, found %d\n", name, found);
+#endif
+ return found;
+}
+
+/*
+ Get the element name within the pipeline
+ Caller is responsible for freezing the allocated memory
+*/
+char *
+mistelix_get_element_name_from_pipeline (GstBin* pipe, char* generic_name)
+{
+ char* result = NULL;
+ gpointer point;
+ GstIterator* it;
+ int len;
+
+ len = strlen (generic_name);
+ it = gst_bin_iterate_elements (pipe);
+
+ while (gst_iterator_next (it, &point) == GST_ITERATOR_OK) {
+ GstElement* element = GST_ELEMENT (point);
+ char* name;
+
+ name = gst_element_get_name (element);
+
+ if (strncmp (name, generic_name, len) == 0) {
+ result = malloc (strlen (name) + 1);
+ strcpy (result, name);
+ break;
+ }
+ }
+#ifdef _DEBUG
+ printf ("Seached %s, found %s\n", generic_name, result);
+#endif
+ return result;
+}
+
+/* Method to launch gstreamer thread. Method signature as required by g_thread_create */
gpointer
mistelix_launch_gstreamer (gpointer data)
{
char desc [1024];
- GstElement *pipe;
- GstElement* elem;
+ GstElement* pipe, *element;
ThreadParams* params = (ThreadParams *) data;
+ gboolean has_audio;
+ GstBus *bus;
+ GstMessage *message;
+ GstStateChangeReturn ret;
+ GstPad* seekable_pad;
+ gpointer point_pad;
+ GstIterator *it_pads;
+ char* element_name;
+ int sink = 0;
+ gboolean vorbis;
+ char media [2048];
mistelix_check_init ();
- // Also mpeg2enc available
- // theoraenc ! oggmux for ogg
- if (params->type == 0) {// Theora
- sprintf (desc,
- "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 !theoraenc ! oggmux !filesink location=%s",
- params->total_frames, params->weight, params->height, params->frames_sec, params->filename);
+ has_audio = (*audio != '\x0');
+
+ if (has_audio) {
+ mistelix_detect_media (audio, media);
+
+ if (strcmp (media, "application/ogg") == 0) {
+ if (mistelix_is_codec ("vorbisdec"))
+ vorbis = TRUE;
+ else
+ has_audio = FALSE;
+ } else {
+ if (strcmp (media, "application/x-id3") == 0) {
+ if (mistelix_is_codec ("flump3dec"))
+ vorbis = FALSE;
+ else
+ has_audio = FALSE;
+ } else {
+ printf ("mistelix: unsupported audio format: %s\n", media);
+ has_audio = FALSE;
+ }
+ }
}
- else{ // DVD
- if (*audio == '\x0') {
- //
- // ffenc_mpeg2video
- // We use 'bitrate'element to set the quality of the generated MPEG2 video.
- // Every additional 10000 bits/s represent approximately 10% additional time
- // to generate the video
- //
- // ffmux_dvd
- // We use maxdelay and preload to generate MPEG2 compatible streams with DVD
- //
- sprintf (desc,
- "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 ! ffenc_mpeg2video bitrate=500000 ! ffmux_dvd preload=500000 maxdelay=699999 !filesink location=%s",
- params->total_frames, params->weight, params->height, params->frames_sec, params->filename);
+
+ if (params->type == 0) {/* Theora output format */
+ if (has_audio) {
+ if (vorbis) { /* Source audio in Vorbis */
+ sprintf (desc,
+ "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 !"
+ "theoraenc ! mux. filesrc location=%s ! oggdemux ! vorbisdec ! audioconvert ! vorbisenc ! "
+ "oggmux name=mux ! filesink location=%s",
+ params->total_frames, params->weight, params->height, params->frames_sec, audio, params->filename);
+ } else { /* Source audio in MP3 */
+ sprintf (desc,
+ "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 !"
+ "theoraenc ! mux. filesrc location=%s ! flump3dec ! audioconvert ! vorbisenc ! "
+ "oggmux name=mux ! filesink location=%s",
+ params->total_frames, params->weight, params->height, params->frames_sec, audio, params->filename);
+ }
} else {
- // ffenc_ac3 does not seem to be recognised by ffmpeg when preparing the final MPEG
sprintf (desc,
- "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 ! ffenc_mpeg2video bitrate=500000 ! queue ! mux. filesrc location=%s ! mad ! audioconvert ! ffenc_mp2 ! ffmux_dvd name=mux preload=500000 maxdelay=699999 ! filesink location=%s",
- params->total_frames, params->weight, params->height, params->frames_sec, audio, params->filename);
+ "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 !"
+ "theoraenc ! oggmux !filesink location=%s",
+ params->total_frames, params->weight, params->height, params->frames_sec, params->filename);
}
}
+ else { /* DVD output format */
+
+ /*
+ * ffenc_mpeg2video
+ * We use 'bitrate'element to set the quality of the generated MPEG2 video.
+ * Every additional 10000 bits/s represent approximately 10% additional time
+ * to generate the video
+ *
+ * ffmux_dvd
+ * We use maxdelay and preload to generate MPEG2 compatible streams with DVD
+ */
+
+ /* No audio support for now */
+ sprintf (desc,
+ "mistelixvideosrc num-buffers=%u ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u,framerate=(fraction)%u/1 !"
+ "ffenc_mpeg2video bitrate=500000 ! ffmux_dvd preload=500000 maxdelay=699999 !filesink location=%s",
+ params->total_frames, params->weight, params->height, params->frames_sec, params->filename);
+ }
#ifdef _DEBUG
printf ("mistelix_launch_gstreamer: %s\n", desc);
#endif
pipe = gst_parse_launch (desc, NULL);
- run_pipeline (pipe);
+ /* Launch pipeline */
+
+#ifdef _DEBUG
+ listelements (GST_BIN (pipe));
+ printf ("*** run_pipeline start\n");
+#endif
+
+ g_assert (pipe);
+ bus = gst_element_get_bus (pipe);
+ g_assert (bus);
+
+ gst_element_set_state (pipe, GST_STATE_PLAYING);
+
+ /* Wait for status change */
+ gst_element_get_state (pipe, NULL, NULL, SEEK_TIMEOUT);
+
+ if (has_audio) {
+
+ /* Find the pad of the audio decoder to send the audio seek events */
+ if (vorbis)
+ element_name = mistelix_get_element_name_from_pipeline (GST_BIN (pipe), "vorbisdec");
+ else
+ element_name = mistelix_get_element_name_from_pipeline (GST_BIN (pipe), "flump3dec");
+
+ g_assert (element_name);
+ element = gst_bin_get_by_name (GST_BIN (pipe), element_name);
+ free (element_name);
+
+ g_assert (element);
+ seekable_pad = gst_element_get_pad (element, "src");
+ g_assert (seekable_pad);
+
+ /* Find the muxer's sink's and set callback function */
+ element = gst_bin_get_by_name (GST_BIN (pipe), "mux");
+ g_assert (element);
+
+ it_pads = gst_element_iterate_pads (element);
+ while (gst_iterator_next (it_pads, &point_pad) == GST_ITERATOR_OK) {
+ GstPad * pad = GST_PAD (point_pad);
+
+ if (strncmp (gst_element_get_name (pad), "src", 3) == 0)
+ continue;
+
+ sink++;
+ if (sink < 2)
+ continue;
+
+ /* The second sink is the video sink */
+ #ifdef _DEBUG
+ printf ("Setting handler for %s\n", gst_element_get_name (pad));
+ #endif
+ gst_pad_set_event_function (pad, gst_handle_sink_event);
+ break;
+ }
+
+ send_seek_event (pipe, seekable_pad, FALSE);
+ }
+ /* We get a GST_MESSAGE_EOS when the pipe is finished */
+ while (1) {
+ GstMessageType revent;
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2);
+
+ if (message) {
+ revent = GST_MESSAGE_TYPE (message);
+#ifdef _DEBUG
+ printf ("*** run_pipeline message: %s (%x)\n", gst_message_type_get_name (revent), revent);
+#endif
+ gst_message_unref (message);
+ } else
+ revent = GST_MESSAGE_UNKNOWN;
+
+ if (revent == GST_MESSAGE_SEGMENT_DONE) {
+ /* Send audio segment again */
+ send_seek_event (pipe, seekable_pad, TRUE);
+ continue;
+ }
+
+ if (revent == GST_MESSAGE_ERROR) {
+#ifdef _DEBUG
+ printf ("*** run_pipeline exiting reason GST_MESSAGE_ERROR\n");
+#endif
+ break;
+ }
+
+ if (revent == GST_MESSAGE_EOS) {
+#ifdef _DEBUG
+ printf ("*** run_pipeline exiting reason: GST_MESSAGE_EOS\n");
+#endif
+ break;
+ }
+ }
+
+ /* Need to explicitly set elements to the NULL state before dropping the final reference */
+ gst_element_set_state (pipe, GST_STATE_NULL);
+ gst_element_get_state (pipe, NULL, NULL, SEEK_TIMEOUT);
+ gst_object_unref (pipe);
+ gst_object_unref (bus);
+
+#ifdef _DEBUG
+ printf ("*** run_pipeline end\n");
+#endif
return NULL;
}
@@ -615,8 +908,6 @@ mistelix_socket_connect ()
printf ("*** mistelix_socket_connect %d\n", mis_socket);
}
-
-
void
mistelix_socket_send (unsigned char* data, unsigned int bytes)
{
@@ -624,6 +915,9 @@ mistelix_socket_send (unsigned char* data, unsigned int bytes)
}
+/*
+ Runs standard pipline that ends by an EOS event
+*/
void
run_pipeline (GstElement * pipe)
{
@@ -640,8 +934,11 @@ run_pipeline (GstElement * pipe)
g_assert (bus);
gst_element_set_state (pipe, GST_STATE_PLAYING);
-
- // We get a GST_MESSAGE_EOS when the pipe is finished
+
+ /* Wait for status change */
+ gst_element_get_state (pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
+
+ /* We get a GST_MESSAGE_EOS when the pipe is finished */
while (1) {
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2);
@@ -651,7 +948,8 @@ run_pipeline (GstElement * pipe)
printf ("*** run_pipeline message: %s (%x)\n", gst_message_type_get_name (revent), revent);
#endif
gst_message_unref (message);
- }
+ } else
+ revent = GST_MESSAGE_UNKNOWN;
if (revent == GST_MESSAGE_ERROR) {
#ifdef _DEBUG
diff --git a/libmistelix/mistelix.h b/libmistelix/mistelix.h
index d37415f..b3bca0d 100644
--- a/libmistelix/mistelix.h
+++ b/libmistelix/mistelix.h
@@ -47,6 +47,8 @@ void mistelix_thumbnail_video (const char* filein, const char* fileout, int widt
// Private (not exposed)
+char * mistelix_get_element_name_from_pipeline (GstBin* pipe, char* generic_name);
+
void mistelix_detect_media (const char* file, char* media);
static void cb_typefound (GstElement *typefind, guint probability, GstCaps *caps, gpointer data);
diff --git a/src/core/Dependencies.cs b/src/core/Dependencies.cs
index 88f54dc..7b83e3c 100644
--- a/src/core/Dependencies.cs
+++ b/src/core/Dependencies.cs
@@ -39,7 +39,9 @@ namespace Mistelix.Core
FFMUX_DVD,
MISTELIXVIDEOSRC,
THEORAENC,
- OGGMUX
+ OGGMUX,
+ VORBISENC,
+ FLUMP3DEC,
};
public struct Dependency
@@ -126,7 +128,7 @@ namespace Mistelix.Core
status_checked = true;
List <string> codecs = MistelixLib.GetCodecs ();
- string [] used = {"ffenc_mpeg2video", "ffmux_dvd", "mistelixvideosrc", "theoraenc", "oggmux"};
+ string [] used = {"ffenc_mpeg2video", "ffmux_dvd", "mistelixvideosrc", "theoraenc", "oggmux", "vorbisdec", "flump3dec"};
bool [] founds = new bool [used.Length];
// TODO: Better a hash table
@@ -158,7 +160,13 @@ namespace Mistelix.Core
DependencyList.Add (new Dependency (used [(int) Codecs.OGGMUX], founds [(int) Codecs.OGGMUX],
Catalog.GetString ("Missing ogg muxer. You need to install GStreamer Base Plugins.")));
- // Check for application
+ DependencyList.Add (new Dependency (used [(int) Codecs.VORBISENC], founds [(int) Codecs.VORBISENC],
+ Catalog.GetString ("Missing Vorbis audio encoder. You need to install GStreamer Base Plugins.")));
+
+ DependencyList.Add (new Dependency (used [(int) Codecs.FLUMP3DEC], founds [(int) Codecs.FLUMP3DEC],
+ Catalog.GetString ("Missing MP3 audio decoder. You need to install Fluendo MP3 decoder GStreamer plugin.")));
+
+ // Check for applications
bool dvdauthor, spumux;
dvdauthor = CheckApp ("dvdauthor");
diff --git a/src/dialogs/AudioSelectionDialog.cs b/src/dialogs/AudioSelectionDialog.cs
index 67c4878..717237c 100644
--- a/src/dialogs/AudioSelectionDialog.cs
+++ b/src/dialogs/AudioSelectionDialog.cs
@@ -46,10 +46,16 @@ namespace Mistelix.Dialogs
public AudioSelectionDialog () : base ("audioselection")
{
audiofile_browser = new BrowseFile (audiofile_hbox, null, true);
+ audiofile_browser.DefaultDirectory = Mistelix.Preferences.GetStringValue (Preferences.AudioDirectoryKey);
+
clean_button = new Gtk.Button (Catalog.GetString ("No audio"));
audiofile_hbox.Add (clean_button);
clean_button.Clicked += new EventHandler (OnButtonClean);
+ Gtk.Box.BoxChild box = (Gtk.Box.BoxChild) audiofile_hbox [clean_button];
+ box.Expand = false;
+ box.Fill = false;
+
clean_button.ShowAll ();
}
diff --git a/src/mistelix.glade b/src/mistelix.glade
index b672c29..18b21ba 100644
--- a/src/mistelix.glade
+++ b/src/mistelix.glade
@@ -4450,6 +4450,7 @@
</widget>
<widget class="GtkDialog" id="audioselection">
+ <property name="width_request">500</property>
<property name="visible">True</property>
<property name="title" translatable="yes">Select audio</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
diff --git a/src/widgets/BrowseFile.cs b/src/widgets/BrowseFile.cs
index 5896ece..abd8380 100644
--- a/src/widgets/BrowseFile.cs
+++ b/src/widgets/BrowseFile.cs
@@ -35,6 +35,7 @@ namespace Mistelix.Widgets
Button browse;
bool browse_file;
Gtk.FileFilter filter;
+ string default_dir;
public virtual event EventHandler FileSelectedChanged;
@@ -67,6 +68,15 @@ namespace Mistelix.Widgets
}
}
+ public string DefaultDirectory {
+ set {
+ if (browse_file == false)
+ throw new InvalidOperationException ("Default directory can only be set when browsing files");
+
+ default_dir = value;
+ }
+ }
+
public Gtk.FileFilter Filter {
set { filter = value; }
}
@@ -77,7 +87,15 @@ namespace Mistelix.Widgets
Catalog.GetString ("Open Location") , null,
browse_file ? FileChooserAction.Open : FileChooserAction.SelectFolder);
- chooser_dialog.SetCurrentFolder (filename.Text);
+
+ if (browse_file) {
+ if (default_dir != null)
+ chooser_dialog.SetCurrentFolder (default_dir);
+ }
+ else {
+ chooser_dialog.SetCurrentFolder (filename.Text);
+ }
+
chooser_dialog.AddButton (Stock.Cancel, ResponseType.Cancel);
chooser_dialog.AddButton (Stock.Open, ResponseType.Ok);
chooser_dialog.DefaultResponse = ResponseType.Ok;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]