[GTK] Wrong ASSERT in AudioDestinationGstreamer::stop
[WebKit-https.git] / Source / WebCore / platform / audio / gstreamer / AudioDestinationGStreamer.cpp
1 /*
2  *  Copyright (C) 2011, 2012 Igalia S.L
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20
21 #if ENABLE(WEB_AUDIO)
22
23 #include "AudioDestinationGStreamer.h"
24
25 #include "AudioChannel.h"
26 #include "AudioSourceProvider.h"
27 #include <wtf/gobject/GOwnPtr.h>
28 #include "GRefPtrGStreamer.h"
29 #include "GStreamerVersioning.h"
30 #include "Logging.h"
31 #include "WebKitWebAudioSourceGStreamer.h"
32 #include <gst/gst.h>
33 #include <gst/pbutils/pbutils.h>
34
35 namespace WebCore {
36
37 // Size of the AudioBus for playback. The webkitwebaudiosrc element
38 // needs to handle this number of frames per cycle as well.
39 const unsigned framesToPull = 128;
40
41 gboolean messageCallback(GstBus*, GstMessage* message, AudioDestinationGStreamer* destination)
42 {
43     return destination->handleMessage(message);
44 }
45
46 PassOwnPtr<AudioDestination> AudioDestination::create(AudioIOCallback& callback, const String&, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate)
47 {
48     // FIXME: make use of inputDeviceId as appropriate.
49
50     // FIXME: Add support for local/live audio input.
51     if (numberOfInputChannels)
52         LOG(Media, "AudioDestination::create(%u, %u, %f) - unhandled input channels", numberOfInputChannels, numberOfOutputChannels, sampleRate);
53
54     // FIXME: Add support for multi-channel (> stereo) output.
55     if (numberOfOutputChannels != 2)
56         LOG(Media, "AudioDestination::create(%u, %u, %f) - unhandled output channels", numberOfInputChannels, numberOfOutputChannels, sampleRate);
57
58     return adoptPtr(new AudioDestinationGStreamer(callback, sampleRate));
59 }
60
61 float AudioDestination::hardwareSampleRate()
62 {
63     return 44100;
64 }
65
66 unsigned long AudioDestination::maxChannelCount()
67 {
68     // FIXME: query the default audio hardware device to return the actual number
69     // of channels of the device. Also see corresponding FIXME in create().
70     return 0;
71 }
72
73 #ifndef GST_API_VERSION_1
74 static void onGStreamerWavparsePadAddedCallback(GstElement*, GstPad* pad, AudioDestinationGStreamer* destination)
75 {
76     destination->finishBuildingPipelineAfterWavParserPadReady(pad);
77 }
78 #endif
79
80 AudioDestinationGStreamer::AudioDestinationGStreamer(AudioIOCallback& callback, float sampleRate)
81     : m_callback(callback)
82     , m_renderBus(2, framesToPull, false)
83     , m_sampleRate(sampleRate)
84     , m_isPlaying(false)
85 {
86     m_pipeline = gst_pipeline_new("play");
87     GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_pipeline));
88     ASSERT(bus);
89     gst_bus_add_signal_watch(bus.get());
90     g_signal_connect(bus.get(), "message", G_CALLBACK(messageCallback), this);
91
92     GstElement* webkitAudioSrc = reinterpret_cast<GstElement*>(g_object_new(WEBKIT_TYPE_WEB_AUDIO_SRC,
93                                                                             "rate", sampleRate,
94                                                                             "bus", &m_renderBus,
95                                                                             "provider", &m_callback,
96                                                                             "frames", framesToPull, NULL));
97
98     GstElement* wavParser = gst_element_factory_make("wavparse", 0);
99
100     m_wavParserAvailable = wavParser;
101     ASSERT_WITH_MESSAGE(m_wavParserAvailable, "Failed to create GStreamer wavparse element");
102     if (!m_wavParserAvailable)
103         return;
104
105 #ifndef GST_API_VERSION_1
106     g_signal_connect(wavParser, "pad-added", G_CALLBACK(onGStreamerWavparsePadAddedCallback), this);
107 #endif
108     gst_bin_add_many(GST_BIN(m_pipeline), webkitAudioSrc, wavParser, NULL);
109     gst_element_link_pads_full(webkitAudioSrc, "src", wavParser, "sink", GST_PAD_LINK_CHECK_NOTHING);
110
111 #ifdef GST_API_VERSION_1
112     GRefPtr<GstPad> srcPad = adoptGRef(gst_element_get_static_pad(wavParser, "src"));
113     finishBuildingPipelineAfterWavParserPadReady(srcPad.get());
114 #endif
115 }
116
117 AudioDestinationGStreamer::~AudioDestinationGStreamer()
118 {
119     GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_pipeline));
120     ASSERT(bus);
121     g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(messageCallback), this);
122     gst_bus_remove_signal_watch(bus.get());
123
124     gst_element_set_state(m_pipeline, GST_STATE_NULL);
125     gst_object_unref(m_pipeline);
126 }
127
128 void AudioDestinationGStreamer::finishBuildingPipelineAfterWavParserPadReady(GstPad* pad)
129 {
130     ASSERT(m_wavParserAvailable);
131
132     GRefPtr<GstElement> audioSink = gst_element_factory_make("autoaudiosink", 0);
133     m_audioSinkAvailable = audioSink;
134
135     if (!audioSink) {
136         LOG_ERROR("Failed to create GStreamer autoaudiosink element");
137         return;
138     }
139
140     // Autoaudiosink does the real sink detection in the GST_STATE_NULL->READY transition
141     // so it's best to roll it to READY as soon as possible to ensure the underlying platform
142     // audiosink was loaded correctly.
143     GstStateChangeReturn stateChangeReturn = gst_element_set_state(audioSink.get(), GST_STATE_READY);
144     if (stateChangeReturn == GST_STATE_CHANGE_FAILURE) {
145         LOG_ERROR("Failed to change autoaudiosink element state");
146         gst_element_set_state(audioSink.get(), GST_STATE_NULL);
147         m_audioSinkAvailable = false;
148         return;
149     }
150
151     GstElement* audioConvert = gst_element_factory_make("audioconvert", 0);
152     gst_bin_add_many(GST_BIN(m_pipeline), audioConvert, audioSink.get(), NULL);
153
154     // Link wavparse's src pad to audioconvert sink pad.
155     GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(audioConvert, "sink"));
156     gst_pad_link_full(pad, sinkPad.get(), GST_PAD_LINK_CHECK_NOTHING);
157
158     // Link audioconvert to audiosink and roll states.
159     gst_element_link_pads_full(audioConvert, "src", audioSink.get(), "sink", GST_PAD_LINK_CHECK_NOTHING);
160     gst_element_sync_state_with_parent(audioConvert);
161     gst_element_sync_state_with_parent(audioSink.leakRef());
162 }
163
164 gboolean AudioDestinationGStreamer::handleMessage(GstMessage* message)
165 {
166     GOwnPtr<GError> error;
167     GOwnPtr<gchar> debug;
168
169     switch (GST_MESSAGE_TYPE(message)) {
170     case GST_MESSAGE_WARNING:
171         gst_message_parse_warning(message, &error.outPtr(), &debug.outPtr());
172         g_warning("Warning: %d, %s. Debug output: %s", error->code,  error->message, debug.get());
173         break;
174     case GST_MESSAGE_ERROR:
175         gst_message_parse_error(message, &error.outPtr(), &debug.outPtr());
176         g_warning("Error: %d, %s. Debug output: %s", error->code,  error->message, debug.get());
177         gst_element_set_state(m_pipeline, GST_STATE_NULL);
178         m_isPlaying = false;
179         break;
180     default:
181         break;
182     }
183     return TRUE;
184 }
185
186 void AudioDestinationGStreamer::start()
187 {
188     ASSERT(m_wavParserAvailable);
189     if (!m_wavParserAvailable)
190         return;
191
192     gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
193     m_isPlaying = true;
194 }
195
196 void AudioDestinationGStreamer::stop()
197 {
198     ASSERT(m_wavParserAvailable && m_audioSinkAvailable);
199     if (!m_wavParserAvailable || !m_audioSinkAvailable)
200         return;
201
202     gst_element_set_state(m_pipeline, GST_STATE_PAUSED);
203     m_isPlaying = false;
204 }
205
206 } // namespace WebCore
207
208 #endif // ENABLE(WEB_AUDIO)