1
/* This file is part of the KDE project
2
Copyright (C) 2004 Max Howell <max.howell@methylblue.com>
3
Copyright (C) 2006 Tim Beaulen <tbscope@gmail.com>
4
Copyright (C) 2009 Martin Sandsmark <sandsmark@samfundet.no>
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public
8
License as published by the Free Software Foundation; either
9
version 2 of the License, or (at your option) any later version.
11
This library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Library General Public License
17
along with this library; see the file COPYING.LIB. If not, write to
18
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
Boston, MA 02110-1301, USA.
23
#include "audiodataoutput.h"
24
#include "mediaobject.h"
33
AudioDataOutputXT::AudioDataOutputXT(AudioDataOutput *output) :
34
SinkNodeXT("AudioDataOutput"),
35
SourceNodeXT("AudioDataOutput"),
40
m_xine = Backend::xine();
44
// Dummy audio port, until we get the proper one
45
xine_audio_port_t *port = xine_open_audio_driver(m_xine, "none", 0);
47
// Allocate a new scope plugin
48
m_plugin = (scope_plugin_t*)qMalloc(sizeof(scope_plugin_t));
50
// It is also a post plugin
51
post_plugin_t *post_plugin = (post_plugin_t*)m_plugin;
53
//1 audio input, 0 video inputs
54
_x_post_init(post_plugin, 1, 0);
56
// Intercept the null audio port (until we get the proper one)
57
intercept(port, true);
59
/* code is straight from xine_init_post()
60
can't use that function as it only dlopens the plugins
61
and our plugin is statically linked in */
62
post_plugin->running_ticket = (*m_xine).port_ticket;
63
post_plugin->xine = m_xine;
65
// Store a reference to our own object in the post plugin struct
66
m_plugin->audioDataOutput = this;
69
AudioDataOutputXT::~AudioDataOutputXT()
71
//xine_post_dispose(m_xine, &((post_plugin_t*)m_plugin)->xine_post); //TODO
75
/// Rewires this node to the specified sourcenode. I don't think this is ever used (properly).
76
void AudioDataOutputXT::rewireTo(SourceNodeXT *source)
78
debug() << Q_FUNC_INFO << "rewiring to " << source;
79
if (!source->audioOutputPort()) { // I can't get no satisfaction
80
debug() << Q_FUNC_INFO << " no audioport in source";
84
// Make sure the source is sane
87
// Get the audio input port in our post plugin
88
xine_post_in_t *target = (xine_post_in_t*)xine_post_input(
89
&((post_plugin_t*)m_plugin)->xine_post,
90
const_cast<char*>("audio in"));
92
if (!xine_post_wire(source->audioOutputPort(), target)) {
93
qWarning() << Q_FUNC_INFO << ": Failed to rewire!";
96
m_postOutput = source->audioOutputPort();
97
m_xtSink->rewireTo(source);
99
// Make sure things went okay
101
SinkNodeXT::assert();
104
/// Returns this Source's audio output port. Don't think this is used either.
105
xine_post_out_t *AudioDataOutputXT::audioOutputPort() const
110
/// Intercepts a given Xine audio port (called from AudioOutput)
111
void AudioDataOutputXT::intercept(xine_audio_port_t *p, bool isNull)
113
if (p == m_audioPort) // we're already intercepting this one
120
post_plugin_t *post_plugin = (post_plugin_t*)m_plugin;
122
// Populate the port with dummy functions
123
post_audio_port_t *port = _x_post_intercept_audio_port(post_plugin, m_audioPort, &input, &output);
125
* Do we leak these ports? Or is the Xine reference counting enough?
126
* Stay tuned for valgrind and interesting questions!
130
qWarning() << Q_FUNC_INFO << "unable to allocate port! (out of memory?)";
135
// Put in our own callbacks
136
port->new_port.open = openPort;
137
port->new_port.close = closePort;
138
port->new_port.put_buffer = putBufferCallback;
140
// Store the audio port for future use
141
m_audioPort = &port->new_port;
143
// Wire in the port input into our post plugin
144
post_plugin->xine_post.audio_input[0] = &port->new_port;
145
post_plugin->xine_post.type = PLUGIN_POST;
148
m_frontend->m_keepInSync = false;
150
m_frontend->m_keepInSync = true;
153
/// Callback function, opens the xine port
154
int AudioDataOutputXT::openPort(xine_audio_port_t *port_gen,
155
xine_stream_t *stream,
160
// Reference to the relevant object
161
AudioDataOutputXT *that = ((scope_plugin_t*)((post_audio_port_t*)port_gen)->post)->audioDataOutput;
163
post_audio_port_t *port = (post_audio_port_t*)port_gen;
165
_x_post_rewire((post_plugin_t*)port->post);
166
_x_post_inc_usage(port);
168
port->stream = stream;
173
// Set the new audio stream parameters
174
that->m_channels = _x_ao_mode2channels(mode);
175
that->m_frontend->setChannels(that->m_channels);
176
that->m_frontend->m_sampleRate = rate;
178
return port->original_port->open(port->original_port, stream, bits, rate, mode);
181
/// Callback function, closes the xine port
182
void AudioDataOutputXT::closePort(xine_audio_port_t *port_gen, xine_stream_t *stream)
184
debug() << Q_FUNC_INFO << " closing port " << port_gen;
185
post_audio_port_t *port = (post_audio_port_t*)port_gen;
187
// This is the same as closing the port, according to comments in the Xine source
189
port->original_port->close(port->original_port, stream);
191
// Decrease the reference counter in the port
192
_x_post_dec_usage(port);
195
/// Callback function, receives audio data
196
void AudioDataOutputXT::putBufferCallback(xine_audio_port_t * port_gen, audio_buffer_t *buf, xine_stream_t *stream)
198
AudioDataOutputXT *that = ((scope_plugin_t*)((post_audio_port_t*)port_gen)->post)->audioDataOutput;
200
// Get the number of samples (audio frames * audio channels)
201
int samples = buf->num_frames * that->m_channels;
203
// Present the audio data to our frontend
204
that->m_frontend->packetReady(samples, buf->mem, buf->vpts);
206
/* Send the audio buffer back to the original port.
207
This notifies Xine that we have finished processing
208
this buffer, so Xine can give us a new one */
209
post_audio_port_t *port = (post_audio_port_t*)port_gen;
210
port->original_port->put_buffer(port->original_port, buf, stream);
214
/* BACKEND-FRONT OBJECT */
215
AudioDataOutput::AudioDataOutput(QObject*)
216
: SinkNode(new AudioDataOutputXT(this))
217
, SourceNode(static_cast<AudioDataOutputXT *>(SinkNode::m_threadSafeObject.data()))
220
m_keepInSync = false;
221
m_sampleRate = 44100;
224
AudioDataOutput::~AudioDataOutput()
226
//K_XT(AudioDataOutput);
230
inline void AudioDataOutput::packetReady(const int samples, const qint16 *buffer, const qint64 vpts)
232
if (m_channels < 0 || m_channels > 2)
235
// Check if it has been cleared
236
if (m_pendingFrames.isEmpty())
237
m_pendingFrames.append(Frame());
239
for (int i=0; i<samples; i++) {
240
if (m_pendingFrames.first().map[Phonon::AudioDataOutput::LeftChannel].size() >= m_dataSize) {
241
m_pendingFrames.prepend(Frame());
242
m_pendingFrames.first().timestamp = vpts;
244
// Tell the QVector how much data we're expecting, speeds things up a bit
245
m_pendingFrames.first().map[Phonon::AudioDataOutput::LeftChannel].reserve(m_dataSize);
247
m_pendingFrames.first().map[Phonon::AudioDataOutput::RightChannel].reserve(m_dataSize);
250
m_pendingFrames.first().map[Phonon::AudioDataOutput::LeftChannel].append(buffer[i]);
252
m_pendingFrames.first().map[Phonon::AudioDataOutput::RightChannel].append(buffer[i++]);
255
// Are we supposed to keep our signals in sync?
257
/*while (m_mediaObject && !m_pendingFrames.isEmpty() &&
258
m_pendingFrames.first().timestamp < m_mediaObject->stream()->currentVpts() &&
259
m_pendingFrames.first().map[Phonon::AudioDataOutput::LeftChannel].size() >= m_dataSize) {
260
emit dataReady(m_pendingFrames.takeFirst().map);
262
for (int i=0; i<m_pendingFrames.size(); i++) {
263
if (m_pendingFrames[i].timestamp < m_mediaObject->stream()->currentVpts() &&
264
m_pendingFrames[i].map[Phonon::AudioDataOutput::LeftChannel].size() >= m_dataSize) {
265
emit dataReady(m_pendingFrames.takeAt(i).map);
268
} else { // Fire at will, as long as there is enough data
269
while (!m_pendingFrames.isEmpty() &&
270
m_pendingFrames.last().map[Phonon::AudioDataOutput::LeftChannel].size() >= m_dataSize)
271
emit dataReady(m_pendingFrames.takeLast().map);
275
/// Handle events (basically just pass it on)
276
void AudioDataOutput::upstreamEvent(Event *e)
279
if (e->type() == Event::IsThereAXineEngineForMe) {
281
MediaObject *mediaObject = dynamic_cast<MediaObject*>(m_source); //TODO; qobject_cast?
283
SourceNode::downstreamEvent(new HeresYourXineStreamEvent(mediaObject->stream()));
284
m_mediaObject = mediaObject;
287
SourceNode::upstreamEvent(e);
290
}} //namespace Phonon::Xine
292
#include "audiodataoutput.moc"