| /* GStreamer |
| * Copyright (C) 2003 Martin Soto <martinsoto@users.sourceforge.net> |
| * |
| * dxr3audiosink.c: Audio sink for em8300 based DVD cards. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| #include <errno.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/ioctl.h> |
| |
| #include <linux/soundcard.h> |
| #include <linux/em8300.h> |
| |
| #include <gst/gst-i18n-plugin.h> |
| #include <gst/gst.h> |
| |
| #include "dxr3audiosink.h" |
| #include "dxr3marshal.h" |
| #include "dxr3common.h" |
| |
| /* Our only supported AC3 byte rate. */ |
| #define AC3_BYTE_RATE 48000 |
| |
| /* Determines the amount of time to play the given number of bytes of |
| the original AC3 stream. The result is expressed as MPEG2. */ |
| #define TIME_FOR_BYTES(bytes) (((bytes) * 90) / 48) |
| |
| /* Dxr3AudioSink signals and args */ |
| enum |
| { |
| SIGNAL_FLUSHED, |
| LAST_SIGNAL |
| }; |
| |
| enum |
| { |
| ARG_0, |
| ARG_DIGITAL_PCM |
| }; |
| |
| static GstStaticPadTemplate dxr3audiosink_pcm_sink_factory = |
| GST_STATIC_PAD_TEMPLATE ("pcm_sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("audio/x-raw-int, " |
| "endianness = (int) BYTE_ORDER, " |
| "signed = (boolean) TRUE, " |
| "width = (int) 16, " |
| "depth = (int) 16, " |
| "rate = (int) { 32000, 44100, 48000, 66000 }, " "channels = (int) 2") |
| ); |
| |
| static GstStaticPadTemplate dxr3audiosink_ac3_sink_factory = |
| GST_STATIC_PAD_TEMPLATE ("ac3_sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("audio/x-ac3" |
| /* no parameters needed, we don't need a parsed stream */ |
| ) |
| ); |
| |
| |
| static void dxr3audiosink_class_init (Dxr3AudioSinkClass * klass); |
| static void dxr3audiosink_base_init (Dxr3AudioSinkClass * klass); |
| static void dxr3audiosink_init (Dxr3AudioSink * sink); |
| |
| static void dxr3audiosink_set_property (GObject * object, |
| guint prop_id, const GValue * value, GParamSpec * pspec); |
| static void dxr3audiosink_get_property (GObject * object, |
| guint prop_id, GValue * value, GParamSpec * pspec); |
| |
| static gboolean dxr3audiosink_open (Dxr3AudioSink * sink); |
| static gboolean dxr3audiosink_set_mode_pcm (Dxr3AudioSink * sink); |
| static gboolean dxr3audiosink_set_mode_ac3 (Dxr3AudioSink * sink); |
| static void dxr3audiosink_close (Dxr3AudioSink * sink); |
| static void dxr3audiosink_set_clock (GstElement * element, GstClock * clock); |
| |
| static GstPadLinkReturn dxr3audiosink_pcm_sinklink (GstPad * pad, |
| const GstCaps * caps); |
| static void dxr3audiosink_set_scr (Dxr3AudioSink * sink, guint32 scr); |
| |
| static gboolean dxr3audiosink_handle_event (GstPad * pad, GstEvent * event); |
| static void dxr3audiosink_chain_pcm (GstPad * pad, GstData * buf); |
| static void dxr3audiosink_chain_ac3 (GstPad * pad, GstData * buf); |
| |
| /* static void dxr3audiosink_wait (Dxr3AudioSink *sink, */ |
| /* GstClockTime time); */ |
| /* static int dxr3audiosink_mvcommand (Dxr3AudioSink *sink, */ |
| /* int command); */ |
| |
| static GstStateChangeReturn dxr3audiosink_change_state (GstElement * element, |
| GstStateChange transition); |
| |
| static void dxr3audiosink_flushed (Dxr3AudioSink * sink); |
| |
| static GstElementClass *parent_class = NULL; |
| static guint dxr3audiosink_signals[LAST_SIGNAL] = { 0 }; |
| |
| |
| extern GType |
| dxr3audiosink_get_type (void) |
| { |
| static GType dxr3audiosink_type = 0; |
| |
| if (!dxr3audiosink_type) { |
| static const GTypeInfo dxr3audiosink_info = { |
| sizeof (Dxr3AudioSinkClass), |
| (GBaseInitFunc) dxr3audiosink_base_init, |
| NULL, |
| (GClassInitFunc) dxr3audiosink_class_init, |
| NULL, |
| NULL, |
| sizeof (Dxr3AudioSink), |
| 0, |
| (GInstanceInitFunc) dxr3audiosink_init, |
| }; |
| |
| dxr3audiosink_type = g_type_register_static (GST_TYPE_ELEMENT, |
| "Dxr3AudioSink", &dxr3audiosink_info, 0); |
| } |
| |
| return dxr3audiosink_type; |
| } |
| |
| |
| static void |
| dxr3audiosink_base_init (Dxr3AudioSinkClass * klass) |
| { |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&dxr3audiosink_pcm_sink_factory)); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&dxr3audiosink_ac3_sink_factory)); |
| gst_element_class_set_details_simple (element_class, |
| "dxr3/Hollywood+ mpeg decoder board audio plugin", "Audio/Sink", |
| "Feeds audio to Sigma Designs em8300 based boards", |
| "Martin Soto <martinsoto@users.sourceforge.net>"); |
| } |
| |
| static void |
| dxr3audiosink_class_init (Dxr3AudioSinkClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| |
| parent_class = g_type_class_peek_parent (klass); |
| |
| dxr3audiosink_signals[SIGNAL_FLUSHED] = |
| g_signal_new ("flushed", G_TYPE_FROM_CLASS (klass), |
| G_SIGNAL_RUN_LAST, |
| G_STRUCT_OFFSET (Dxr3AudioSinkClass, flushed), |
| NULL, NULL, dxr3_marshal_VOID__VOID, G_TYPE_NONE, 0); |
| |
| klass->flushed = dxr3audiosink_flushed; |
| |
| g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DIGITAL_PCM, |
| g_param_spec_boolean ("digital-pcm", "Digital PCM", |
| "Use the digital output for PCM sound", FALSE, G_PARAM_READWRITE)); |
| |
| gobject_class->set_property = dxr3audiosink_set_property; |
| gobject_class->get_property = dxr3audiosink_get_property; |
| |
| gstelement_class->change_state = dxr3audiosink_change_state; |
| gstelement_class->set_clock = dxr3audiosink_set_clock; |
| } |
| |
| |
| static void |
| dxr3audiosink_init (Dxr3AudioSink * sink) |
| { |
| GstPadTemplate *temp; |
| |
| /* Create the PCM pad. */ |
| temp = gst_static_pad_template_get (&dxr3audiosink_pcm_sink_factory); |
| sink->pcm_sinkpad = gst_pad_new_from_template (temp, "pcm_sink"); |
| gst_pad_set_chain_function (sink->pcm_sinkpad, dxr3audiosink_chain_pcm); |
| gst_pad_set_link_function (sink->pcm_sinkpad, dxr3audiosink_pcm_sinklink); |
| gst_element_add_pad (GST_ELEMENT (sink), sink->pcm_sinkpad); |
| |
| /* Create the AC3 pad. */ |
| temp = gst_static_pad_template_get (&dxr3audiosink_ac3_sink_factory); |
| sink->ac3_sinkpad = gst_pad_new_from_template (temp, "ac3_sink"); |
| gst_pad_set_chain_function (sink->ac3_sinkpad, dxr3audiosink_chain_ac3); |
| gst_element_add_pad (GST_ELEMENT (sink), sink->ac3_sinkpad); |
| |
| GST_OBJECT_FLAG_SET (GST_ELEMENT (sink), GST_ELEMENT_EVENT_AWARE); |
| |
| sink->card_number = 0; |
| |
| sink->audio_filename = NULL; |
| sink->audio_fd = -1; |
| |
| sink->control_filename = NULL; |
| sink->control_fd = -1; |
| |
| /* Since we don't know any better, we set the initial scr to 0. */ |
| sink->scr = 0; |
| |
| /* Initially don't use digital output. */ |
| sink->digital_pcm = FALSE; |
| |
| /* Initially there's no padder. */ |
| sink->padder = NULL; |
| |
| sink->mode = DXR3AUDIOSINK_MODE_NONE; |
| } |
| |
| |
| static void |
| dxr3audiosink_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| Dxr3AudioSink *sink; |
| |
| sink = DXR3AUDIOSINK (object); |
| |
| switch (prop_id) { |
| case ARG_DIGITAL_PCM: |
| sink->digital_pcm = g_value_get_boolean (value); |
| /* Refresh the setup of the device. */ |
| if (sink->mode == DXR3AUDIOSINK_MODE_PCM) { |
| dxr3audiosink_set_mode_pcm (sink); |
| } |
| g_object_notify (G_OBJECT (sink), "digital-pcm"); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| |
| static void |
| dxr3audiosink_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| Dxr3AudioSink *sink; |
| |
| g_return_if_fail (GST_IS_DXR3AUDIOSINK (object)); |
| |
| sink = DXR3AUDIOSINK (object); |
| |
| switch (prop_id) { |
| case ARG_DIGITAL_PCM: |
| g_value_set_boolean (value, sink->digital_pcm); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| |
| static gboolean |
| dxr3audiosink_open (Dxr3AudioSink * sink) |
| { |
| g_return_val_if_fail (!GST_OBJECT_FLAG_IS_SET (sink, DXR3AUDIOSINK_OPEN), |
| FALSE); |
| |
| /* Compute the name of the audio device file. */ |
| sink->audio_filename = g_strdup_printf ("/dev/em8300_ma-%d", |
| sink->card_number); |
| |
| sink->audio_fd = open (sink->audio_filename, O_WRONLY); |
| if (sink->audio_fd < 0) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, |
| (_("Could not open audio device \"%s\" for writing."), |
| sink->audio_filename), GST_ERROR_SYSTEM); |
| return FALSE; |
| } |
| |
| /* Open the control device. */ |
| sink->control_filename = g_strdup_printf ("/dev/em8300-%d", |
| sink->card_number); |
| |
| sink->control_fd = open (sink->control_filename, O_WRONLY); |
| if (sink->control_fd < 0) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, |
| (_("Could not open control device \"%s\" for writing."), |
| sink->control_filename), GST_ERROR_SYSTEM); |
| return FALSE; |
| } |
| |
| GST_OBJECT_FLAG_SET (sink, DXR3AUDIOSINK_OPEN); |
| |
| dxr3audiosink_set_mode_pcm (sink); |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| * dxr3audiosink_set_mode: |
| * @sink: The sink element to operate on. |
| * |
| * Set the operation mode of the element to PCM. |
| */ |
| static gboolean |
| dxr3audiosink_set_mode_pcm (Dxr3AudioSink * sink) |
| { |
| int tmp, oss_mode, audiomode; |
| |
| if (sink->audio_fd == -1 || sink->control_fd == -1) { |
| return FALSE; |
| } |
| |
| /* Set the audio device mode. */ |
| oss_mode = (G_BYTE_ORDER == G_BIG_ENDIAN ? AFMT_S16_BE : AFMT_S16_LE); |
| tmp = oss_mode; |
| if (ioctl (sink->audio_fd, SNDCTL_DSP_SETFMT, &tmp) < 0 || tmp != oss_mode) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, |
| (_("Could not configure audio device \"%s\"."), sink->audio_filename), |
| GST_ERROR_SYSTEM); |
| return FALSE; |
| } |
| |
| /* Set the card's general audio output mode. */ |
| audiomode = sink->digital_pcm ? |
| EM8300_AUDIOMODE_DIGITALPCM : EM8300_AUDIOMODE_ANALOG; |
| ioctl (sink->control_fd, EM8300_IOCTL_SET_AUDIOMODE, &audiomode); |
| |
| /* Set the sampling rate. */ |
| tmp = sink->rate; |
| if (ioctl (sink->audio_fd, SNDCTL_DSP_SPEED, &tmp) < 0) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, |
| (_("Could not set audio device \"%s\" to %d Hz."), sink->audio_filename, |
| sink->rate), GST_ERROR_SYSTEM); |
| return FALSE; |
| } |
| |
| /* Get rid of the padder, if any. */ |
| if (sink->padder != NULL) { |
| g_free (sink->padder); |
| sink->padder = NULL; |
| } |
| |
| sink->mode = DXR3AUDIOSINK_MODE_PCM; |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| * dxr3audiosink_set_mode: |
| * @sink: The sink element to operate on |
| * |
| * Set the operation mode of the element to AC3. |
| */ |
| static gboolean |
| dxr3audiosink_set_mode_ac3 (Dxr3AudioSink * sink) |
| { |
| int tmp, audiomode; |
| |
| if (sink->audio_fd == -1 || sink->control_fd == -1) { |
| return FALSE; |
| } |
| |
| /* Set the sampling rate. */ |
| tmp = AC3_BYTE_RATE; |
| if (ioctl (sink->audio_fd, SNDCTL_DSP_SPEED, &tmp) < 0 || |
| tmp != AC3_BYTE_RATE) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, |
| (_("Could not set audio device \"%s\" to %d Hz."), sink->audio_filename, |
| AC3_BYTE_RATE), GST_ERROR_SYSTEM); |
| return FALSE; |
| } |
| |
| /* Set the card's general audio output mode to AC3. */ |
| audiomode = EM8300_AUDIOMODE_DIGITALAC3; |
| ioctl (sink->control_fd, EM8300_IOCTL_SET_AUDIOMODE, &audiomode); |
| |
| /* Create a padder if necessary, */ |
| if (sink->padder == NULL) { |
| sink->padder = g_malloc (sizeof (ac3_padder)); |
| ac3p_init (sink->padder); |
| } |
| |
| sink->mode = DXR3AUDIOSINK_MODE_AC3; |
| |
| return TRUE; |
| } |
| |
| |
| static void |
| dxr3audiosink_close (Dxr3AudioSink * sink) |
| { |
| g_return_if_fail (GST_OBJECT_FLAG_IS_SET (sink, DXR3AUDIOSINK_OPEN)); |
| |
| if (close (sink->audio_fd) != 0) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, |
| (_("Could not close audio device \"%s\"."), sink->audio_filename), |
| GST_ERROR_SYSTEM); |
| return; |
| } |
| |
| if (close (sink->control_fd) != 0) { |
| GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, |
| (_("Could not close control device \"%s\"."), sink->audio_filename), |
| GST_ERROR_SYSTEM); |
| return; |
| } |
| |
| GST_OBJECT_FLAG_UNSET (sink, DXR3AUDIOSINK_OPEN); |
| |
| g_free (sink->audio_filename); |
| sink->audio_filename = NULL; |
| |
| g_free (sink->control_filename); |
| sink->control_filename = NULL; |
| |
| /* Get rid of the padder, if any. */ |
| if (sink->padder != NULL) { |
| g_free (sink->padder); |
| sink->padder = NULL; |
| } |
| } |
| |
| |
| static void |
| dxr3audiosink_set_clock (GstElement * element, GstClock * clock) |
| { |
| Dxr3AudioSink *src = DXR3AUDIOSINK (element); |
| |
| src->clock = clock; |
| } |
| |
| |
| static GstPadLinkReturn |
| dxr3audiosink_pcm_sinklink (GstPad * pad, const GstCaps * caps) |
| { |
| Dxr3AudioSink *sink = DXR3AUDIOSINK (gst_pad_get_parent (pad)); |
| GstStructure *structure = gst_caps_get_structure (caps, 0); |
| gint rate; |
| |
| if (!gst_caps_is_fixed (caps)) { |
| return GST_PAD_LINK_DELAYED; |
| } |
| |
| gst_structure_get_int (structure, "rate", &rate); |
| sink->rate = rate; |
| |
| return GST_PAD_LINK_OK; |
| } |
| |
| |
| static void |
| dxr3audiosink_set_scr (Dxr3AudioSink * sink, guint32 scr) |
| { |
| guint32 zero = 0; |
| |
| /* fprintf (stderr, "====== Adjusting SCR\n"); */ |
| ioctl (sink->control_fd, EM8300_IOCTL_SCR_SET, &zero); |
| ioctl (sink->control_fd, EM8300_IOCTL_SCR_SET, &scr); |
| } |
| |
| |
| static gboolean |
| dxr3audiosink_handle_event (GstPad * pad, GstEvent * event) |
| { |
| GstEventType type; |
| Dxr3AudioSink *sink = DXR3AUDIOSINK (gst_pad_get_parent (pad)); |
| |
| type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; |
| |
| switch (type) { |
| case GST_EVENT_FLUSH: |
| if (sink->control_fd >= 0) { |
| unsigned audiomode; |
| |
| if (sink->mode == DXR3AUDIOSINK_MODE_AC3) { |
| audiomode = EM8300_AUDIOMODE_DIGITALPCM; |
| ioctl (sink->control_fd, EM8300_IOCTL_SET_AUDIOMODE, &audiomode); |
| audiomode = EM8300_AUDIOMODE_DIGITALAC3; |
| ioctl (sink->control_fd, EM8300_IOCTL_SET_AUDIOMODE, &audiomode); |
| } |
| |
| /* Report the flush operation. */ |
| g_signal_emit (G_OBJECT (sink), |
| dxr3audiosink_signals[SIGNAL_FLUSHED], 0); |
| } |
| break; |
| default: |
| gst_pad_event_default (pad, event); |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| |
| static void |
| dxr3audiosink_chain_pcm (GstPad * pad, GstData * _data) |
| { |
| Dxr3AudioSink *sink; |
| gint bytes_written = 0; |
| GstBuffer *buf; |
| |
| g_return_if_fail (pad != NULL); |
| g_return_if_fail (GST_IS_PAD (pad)); |
| g_return_if_fail (_data != NULL); |
| |
| sink = DXR3AUDIOSINK (gst_pad_get_parent (pad)); |
| |
| if (GST_IS_EVENT (_data)) { |
| dxr3audiosink_handle_event (pad, GST_EVENT (_data)); |
| return; |
| } |
| |
| buf = GST_BUFFER (_data); |
| |
| if (sink->mode != DXR3AUDIOSINK_MODE_PCM) { |
| /* Switch to PCM mode. */ |
| dxr3audiosink_set_mode_pcm (sink); |
| } |
| |
| if (GST_OBJECT_FLAG_IS_SET (sink, DXR3AUDIOSINK_OPEN)) { |
| if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { |
| /* We have a new scr value. */ |
| sink->scr = GSTTIME_TO_MPEGTIME (GST_BUFFER_TIMESTAMP (buf)); |
| } |
| |
| /* Update the system reference clock (SCR) in the card. */ |
| { |
| unsigned in, out, odelay; |
| unsigned diff; |
| |
| ioctl (sink->control_fd, EM8300_IOCTL_SCR_GET, &out); |
| |
| ioctl (sink->audio_fd, SNDCTL_DSP_GETODELAY, &odelay); |
| |
| in = MPEGTIME_TO_DXRTIME (sink->scr - (odelay * 90) / 192); |
| diff = in > out ? in - out : out - in; |
| if (diff > 1800) { |
| dxr3audiosink_set_scr (sink, in); |
| } |
| } |
| |
| /* Update our SCR value. */ |
| sink->scr += (unsigned) (GST_BUFFER_SIZE (buf) * |
| (90000.0 / ((float) sink->rate * 4))); |
| |
| /* Write the buffer to the sound device. */ |
| bytes_written = write (sink->audio_fd, GST_BUFFER_DATA (buf), |
| GST_BUFFER_SIZE (buf)); |
| if (bytes_written < GST_BUFFER_SIZE (buf)) { |
| fprintf (stderr, "dxr3audiosink: Warning: %d bytes should be " |
| "written, only %d bytes written\n", |
| GST_BUFFER_SIZE (buf), bytes_written); |
| } |
| } |
| |
| gst_buffer_unref (buf); |
| } |
| |
| |
| static void |
| dxr3audiosink_chain_ac3 (GstPad * pad, GstData * _data) |
| { |
| Dxr3AudioSink *sink; |
| gint bytes_written = 0; |
| GstBuffer *buf; |
| |
| g_return_if_fail (pad != NULL); |
| g_return_if_fail (GST_IS_PAD (pad)); |
| g_return_if_fail (_data != NULL); |
| |
| sink = DXR3AUDIOSINK (gst_pad_get_parent (pad)); |
| |
| if (GST_IS_EVENT (_data)) { |
| dxr3audiosink_handle_event (pad, GST_EVENT (_data)); |
| return; |
| } |
| |
| buf = GST_BUFFER (_data); |
| |
| if (sink->mode != DXR3AUDIOSINK_MODE_AC3) { |
| /* Switch to AC3 mode. */ |
| dxr3audiosink_set_mode_ac3 (sink); |
| } |
| |
| if (GST_OBJECT_FLAG_IS_SET (sink, DXR3AUDIOSINK_OPEN)) { |
| int event; |
| |
| if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { |
| /* We have a new scr value. */ |
| |
| /* fprintf (stderr, "------ Audio Time %.04f\n", */ |
| /* (double) GST_BUFFER_TIMESTAMP (buf) / GST_SECOND); */ |
| |
| sink->scr = GSTTIME_TO_MPEGTIME (GST_BUFFER_TIMESTAMP (buf)); |
| } |
| |
| /* Push the new data into the padder. */ |
| ac3p_push_data (sink->padder, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); |
| |
| /* Parse the data. */ |
| event = ac3p_parse (sink->padder); |
| while (event != AC3P_EVENT_PUSH) { |
| switch (event) { |
| case AC3P_EVENT_FRAME: |
| /* We have a new frame: */ |
| |
| /* Update the system reference clock (SCR) in the card. */ |
| { |
| unsigned in, out, odelay; |
| unsigned diff; |
| |
| ioctl (sink->control_fd, EM8300_IOCTL_SCR_GET, &out); |
| |
| ioctl (sink->audio_fd, SNDCTL_DSP_GETODELAY, &odelay); |
| /* 192000 bytes/sec */ |
| |
| in = MPEGTIME_TO_DXRTIME (sink->scr - (odelay * 90) / 192); |
| diff = in > out ? in - out : out - in; |
| if (diff > 1800) { |
| dxr3audiosink_set_scr (sink, in); |
| } |
| } |
| |
| /* Update our SCR value. */ |
| sink->scr += TIME_FOR_BYTES (ac3p_frame_size (sink->padder)); |
| |
| /* Write the frame to the sound device. */ |
| bytes_written = write (sink->audio_fd, ac3p_frame (sink->padder), |
| AC3P_IEC_FRAME_SIZE); |
| |
| if (bytes_written < AC3P_IEC_FRAME_SIZE) { |
| fprintf (stderr, "dxr3audiosink: Warning: %d bytes should be " |
| "written, only %d bytes written\n", |
| AC3P_IEC_FRAME_SIZE, bytes_written); |
| } |
| |
| break; |
| } |
| |
| event = ac3p_parse (sink->padder); |
| } |
| } |
| |
| gst_buffer_unref (buf); |
| } |
| |
| #if 0 |
| /** |
| * dxr3audiosink_wait: |
| * |
| * Make the sink wait the specified amount of time. |
| */ |
| static void |
| dxr3audiosink_wait (Dxr3AudioSink * sink, GstClockTime time) |
| { |
| GstClockID id; |
| GstClockTimeDiff jitter; |
| GstClockReturn ret; |
| GstClockTime current_time = gst_clock_get_time (sink->clock); |
| |
| id = gst_clock_new_single_shot_id (sink->clock, current_time + time); |
| ret = gst_clock_id_wait (id, &jitter); |
| gst_clock_id_free (id); |
| } |
| |
| |
| static int |
| dxr3audiosink_mvcommand (Dxr3AudioSink * sink, int command) |
| { |
| em8300_register_t regs; |
| |
| regs.microcode_register = 1; |
| regs.reg = 0; |
| regs.val = command; |
| |
| return ioctl (sink->control_fd, EM8300_IOCTL_WRITEREG, ®s); |
| } |
| #endif |
| |
| static GstStateChangeReturn |
| dxr3audiosink_change_state (GstElement * element, GstStateChange transition) |
| { |
| g_return_val_if_fail (GST_IS_DXR3AUDIOSINK (element), |
| GST_STATE_CHANGE_FAILURE); |
| |
| if (GST_STATE_PENDING (element) == GST_STATE_NULL) { |
| if (GST_OBJECT_FLAG_IS_SET (element, DXR3AUDIOSINK_OPEN)) { |
| dxr3audiosink_close (DXR3AUDIOSINK (element)); |
| } |
| } else { |
| if (!GST_OBJECT_FLAG_IS_SET (element, DXR3AUDIOSINK_OPEN)) { |
| if (!dxr3audiosink_open (DXR3AUDIOSINK (element))) { |
| return GST_STATE_CHANGE_FAILURE; |
| } |
| } |
| } |
| |
| if (GST_ELEMENT_CLASS (parent_class)->change_state) { |
| return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
| } |
| |
| return GST_STATE_CHANGE_SUCCESS; |
| } |
| |
| |
| /** |
| * dxr3audiosink_flushed: |
| * |
| * Default do nothing implementation for the "flushed" signal. The |
| * "flushed" signal will be fired right after flushing the hardware |
| * queues due to a received flush event |
| */ |
| static void |
| dxr3audiosink_flushed (Dxr3AudioSink * sink) |
| { |
| /* Do nothing. */ |
| } |