~ubuntu-branches/ubuntu/hoary/kdemultimedia/hoary

« back to all changes in this revision

Viewing changes to oggvorbis_artsplugin/oggPlayObject_impl.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Martin Schulze
  • Date: 2003-01-22 15:00:51 UTC
  • Revision ID: james.westby@ubuntu.com-20030122150051-uihwkdoxf15mi1tn
Tags: upstream-2.2.2
ImportĀ upstreamĀ versionĀ 2.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: oggPlayObject_impl.cpp,v 1.2 2001/04/05 12:07:30 garbanzo Exp $ */
 
2
 
 
3
#include <sys/types.h>
 
4
#include <sys/ipc.h>
 
5
#include <sys/sem.h>
 
6
#include <sys/stat.h>
 
7
#include <sys/shm.h>
 
8
#include <sys/wait.h>
 
9
 
 
10
#include <assert.h>
 
11
#include <errno.h>
 
12
#include <fcntl.h>
 
13
#include <math.h>
 
14
#include <signal.h>
 
15
#include <stdio.h>
 
16
#include <stdlib.h>
 
17
#include <string.h>
 
18
 
 
19
#include <stdio.h>
 
20
#include <stdlib.h>
 
21
#include <math.h>
 
22
#include <vorbis/codec.h>
 
23
#include <vorbis/vorbisfile.h>
 
24
 
 
25
/////////////////////////////////////////////////////////////
 
26
// aRts interface
 
27
 
 
28
#include <stdsynthmodule.h>
 
29
#include "oggarts.h"
 
30
#include <convert.h>
 
31
#include <debug.h>
 
32
 
 
33
 
 
34
#include "oggPlayObject_impl.h"
 
35
 
 
36
using namespace Arts;
 
37
 
 
38
oggPlayObject_impl::oggPlayObject_impl()
 
39
{
 
40
        struct shmid_ds bleh;
 
41
        shm_id = shmget(IPC_PRIVATE, sizeof(*shm_buf), 0600);
 
42
        shm_buf = (struct buf_t *)shmat(shm_id, 0, 0);
 
43
 
 
44
        // mark it to be destroyed after the last detach
 
45
        shmctl(shm_id, IPC_RMID, &bleh);
 
46
 
 
47
        // sem0 has base
 
48
        // sem1 remaining space
 
49
        // sem2 seekTo
 
50
        buflen_sem = semget(IPC_PRIVATE, 4, 0600);
 
51
        child_pid = 0;
 
52
}
 
53
 
 
54
oggPlayObject_impl::~oggPlayObject_impl()
 
55
{
 
56
        halt();
 
57
        union semun foo;
 
58
        arts_debug("oggvorbis: removing IPC resources");
 
59
        semctl(buflen_sem, 0, IPC_RMID, foo);
 
60
}
 
61
 
 
62
bool oggPlayObject_impl::loadMedia(const string &filename)
 
63
{
 
64
        halt(); // stop playing any previous stream
 
65
 
 
66
        //throw off a process to handle the decoding
 
67
        //fill the buffers first...
 
68
 
 
69
        short decode_buf[BACKBUFSIZ];
 
70
 
 
71
        struct sembuf semoper;
 
72
        semoper.sem_flg = 0;
 
73
 
 
74
        buf_pos = 0;
 
75
        // setup the initial semaphore joy
 
76
 
 
77
        union semun foo;
 
78
 
 
79
        foo.val = 0;
 
80
        if (semctl(buflen_sem, 0, SETVAL, foo))
 
81
                arts_debug("oggvorbis: couldn't clear queue %d, %s", errno, strerror(errno)); // no data in the queue
 
82
 
 
83
        foo.val = 0;
 
84
        // seekTo is -1 (ie, no seek)
 
85
        if (semctl(buflen_sem, 2, SETVAL, foo))
 
86
                arts_debug("oggvorbis: couldn't clear seekTo, %s",strerror(errno));
 
87
 
 
88
        semctl(buflen_sem, 3, SETVAL, foo);
 
89
        arts_debug("oggvorbis: seekTo is %d", semctl(buflen_sem, 2, GETVAL, foo));
 
90
 
 
91
        // setup the starting free space in the buffer
 
92
        foo.val = BACKBUFSIZ;
 
93
        if (semctl(buflen_sem, 1, SETVAL, foo))
 
94
                arts_debug("oggvorbis: couldn't mark buffer empty");
 
95
 
 
96
        FILE *ogg_fp;
 
97
        ogg_fp = fopen(filename.c_str(), "r");
 
98
        int current_section=0;
 
99
 
 
100
        ov_open(ogg_fp, &vf, NULL, 0);
 
101
        if (!(child_pid = fork())) {
 
102
                arts_debug("oggvorbis: child process");
 
103
                do {
 
104
                        // get more data
 
105
 
 
106
                        int seekTo = semctl(buflen_sem, 2, GETVAL, foo);
 
107
                        if (seekTo) {
 
108
                                arts_debug("oggvorbis: seeking to %d", seekTo);
 
109
                                // we have a seek command
 
110
                                int ret = ov_time_seek(&vf, (double)seekTo-1);
 
111
                                arts_debug("oggvorbis: ov_time_seek returned: %d\n", ret);
 
112
                                foo.val=0;
 
113
                                semctl(buflen_sem, 2, SETVAL, foo); // we've done it
 
114
                        }
 
115
                        foo.val=(long)ov_time_tell(&vf);
 
116
                        if (foo.val == -1) foo.val=0;
 
117
                        semctl(buflen_sem, 3, SETVAL, foo);
 
118
 
 
119
                        int thisPass = ov_read(&vf, (char *)decode_buf, BACKBUFSIZ*sizeof(short), 0, 2, 1, &current_section);
 
120
                        if (thisPass == 0) {
 
121
                                // we're done, or we errored (in which case we're done)
 
122
                                break;
 
123
                        }
 
124
                        thisPass = (thisPass / 4);
 
125
 
 
126
 
 
127
                        semoper.sem_num = 1;
 
128
                        semoper.sem_op = -thisPass;
 
129
                        semop(buflen_sem, &semoper, 1);
 
130
 
 
131
                        // block until there's enough space to stick in this frame
 
132
                        int roomFor = semctl(buflen_sem, 1, GETVAL, foo);
 
133
                        if (roomFor > BACKBUFSIZ) {
 
134
                                arts_debug("oggvorbis: exit requested, bye!");
 
135
                                // this can never go above BACKBUFSIZ in normal operation,
 
136
                                // the consumer wants us to exit
 
137
                                break;
 
138
                        }
 
139
 
 
140
                        for (int i=0 ; i <thisPass ; ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
 
141
                                shm_buf->left[buf_pos] = conv_16le_float(decode_buf[2*i]);
 
142
                                shm_buf->right[buf_pos] = conv_16le_float(decode_buf[2*i+1]);
 
143
                        }
 
144
 
 
145
                        //arts_debug("oggvorbis: enqueued them");
 
146
                        semoper.sem_num = 0;
 
147
                        semoper.sem_op = thisPass;
 
148
                        semop(buflen_sem, &semoper, 1); // mark the additional data now available
 
149
 
 
150
                        //arts_debug("oggvorbis: calculated %d more samples",shm_buf->back$
 
151
                } while(1);
 
152
 
 
153
                //signal completion
 
154
                foo.val = 0;
 
155
                // no more data available
 
156
                semctl(buflen_sem, 0, SETVAL, foo);
 
157
                // and no room either (ie, none coming)
 
158
                semctl(buflen_sem, 1, SETVAL, foo);
 
159
 
 
160
                arts_debug("oggvorbis: decoder process exiting");
 
161
                exit(0);
 
162
        }
 
163
        return true;
 
164
}
 
165
 
 
166
string oggPlayObject_impl::description()
 
167
{
 
168
        return "ogg/vorbis artsplugin - this is my w00t!";
 
169
}
 
170
 
 
171
poTime oggPlayObject_impl::currentTime()
 
172
{
 
173
        poTime time;
 
174
        union semun foo;
 
175
        foo.val=0;
 
176
        time.seconds = semctl(buflen_sem, 3, GETVAL, foo);
 
177
        if (time.seconds == -1)
 
178
                time.seconds = 0; // Eek infinite loop time.
 
179
        time.ms=0;
 
180
        //arts_debug("oggvorbis: time is now %d\n", time.seconds);
 
181
        return time;
 
182
}
 
183
 
 
184
poTime oggPlayObject_impl::overallTime() {
 
185
        poTime time;
 
186
        time.seconds=(long)ov_time_total(&vf, -1);
 
187
        time.ms=0;
 
188
        return time;
 
189
}
 
190
 
 
191
poCapabilities oggPlayObject_impl::capabilities() {
 
192
        return static_cast<poCapabilities>(capPause|capSeek);
 
193
}
 
194
 
 
195
string oggPlayObject_impl::mediaName() {
 
196
        return "";
 
197
}
 
198
 
 
199
poState oggPlayObject_impl::state() {
 
200
        return mState;
 
201
}
 
202
 
 
203
void oggPlayObject_impl::play() {
 
204
        arts_debug("oggvorbis: play");
 
205
        mState = posPlaying;
 
206
}
 
207
 
 
208
void oggPlayObject_impl::halt()
 
209
{
 
210
        mState = posIdle;
 
211
        union semun foo;
 
212
 
 
213
        if (child_pid) {
 
214
                arts_debug("oggvorbis: killing decoder process");
 
215
                foo.val = 2*BACKBUFSIZ;
 
216
                semctl(buflen_sem, 1, SETVAL, foo);
 
217
                waitpid(child_pid, NULL, 0);
 
218
                child_pid = 0;
 
219
        }
 
220
        // tell the producer to exit (would also result in us halting, if we weren't already)
 
221
 
 
222
        // mainly this is to ensure that the decoder wakes up to notice
 
223
}
 
224
 
 
225
void oggPlayObject_impl::seek(const class poTime &t)
 
226
{
 
227
        union semun foo;
 
228
 
 
229
        // this index is one-based so 0 can represent no seek
 
230
        foo.val = static_cast<int>(t.seconds);
 
231
        arts_debug("requesting seek to %d", foo.val);
 
232
        semctl(buflen_sem, 2, SETVAL, foo); // we've done it
 
233
}
 
234
 
 
235
void oggPlayObject_impl::pause()
 
236
{
 
237
        mState = posPaused;
 
238
}
 
239
 
 
240
void oggPlayObject_impl::streamInit()
 
241
{
 
242
        arts_debug("oggvorbis: streamInit");
 
243
}
 
244
 
 
245
void oggPlayObject_impl::streamStart()
 
246
{
 
247
        arts_debug("ogg_vorbis: streamStart");
 
248
}
 
249
 
 
250
void oggPlayObject_impl::calculateBlock(unsigned long samples)
 
251
{
 
252
        int samplesAvailable = 0;
 
253
 
 
254
        //arts_debug("oggvorbis: calculateBlock");
 
255
 
 
256
        if (mState == posPlaying) {
 
257
                //arts_debug("oggvorbis: calculateBlock, %d(%d) of %d samples in buffer",
 
258
                //shm_buf->buflen - bufpos, shm_buf->backbuflen,samples);
 
259
 
 
260
                struct sembuf bleh;
 
261
 
 
262
                bleh.sem_num = 0;
 
263
                bleh.sem_flg = IPC_NOWAIT;
 
264
                //arts_debug("oggvorbis: %d samples wanted", samplesAvailable);
 
265
                bleh.sem_op = -samples; // does the buffer have sufficient samples?
 
266
                if (semop(buflen_sem, &bleh, 1) == -1) {
 
267
                        if (errno == EAGAIN) {
 
268
                                union semun foo;
 
269
                                arts_debug("oggvorbis: buffer underrun");
 
270
                                samplesAvailable = semctl(buflen_sem, 0, GETVAL, foo);
 
271
                                // no samples AND no room is the decoder's way of signalling completion
 
272
                                if (semctl(buflen_sem, 1, GETVAL, foo) == 0) {
 
273
                                        halt();
 
274
                                        samplesAvailable = 0;
 
275
                                }
 
276
                        } else {
 
277
                                // something awful has happened
 
278
                                halt();
 
279
                                samplesAvailable = 0;
 
280
                        }
 
281
                } else {
 
282
                        samplesAvailable = samples; // number of samples we pushed from buffers
 
283
                        // used to calculate the number we should zero out for an underrun
 
284
                }
 
285
                bleh.sem_flg = 0; // back to normal now
 
286
 
 
287
                //arts_debug("oggvorbis: %d samples available",samplesAvailable);
 
288
                for (int i = 0; i < samplesAvailable;
 
289
                        ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
 
290
 
 
291
                        left[i] = shm_buf->left[buf_pos];
 
292
                        right[i] = shm_buf->right[buf_pos];
 
293
                }
 
294
 
 
295
                bleh.sem_num = 1;
 
296
                bleh.sem_op = samplesAvailable;
 
297
                semop(buflen_sem, &bleh, 1); // mark the now-free space
 
298
        }
 
299
        // zero out any samples we didn't have enough to complete
 
300
        while(static_cast<unsigned long>(samplesAvailable) < samples) {
 
301
                left[samplesAvailable] = 0.0;
 
302
                right[samplesAvailable] = 0.0;
 
303
                samplesAvailable++;
 
304
        }
 
305
}
 
306
 
 
307
void oggPlayObject_impl::streamEnd()
 
308
{
 
309
        arts_debug("oggvorbis: streamEnd");
 
310
}
 
311
 
 
312
REGISTER_IMPLEMENTATION(oggPlayObject_impl);