Statistics
| Branch: | Revision:

ffmpeg / libavformat / beosaudio.cpp @ 75b5b631

History | View | Annotate | Download (12.8 KB)

1 dfdfa47c François Revol
/*
2
 * BeOS audio play interface
3
 * Copyright (c) 2000, 2001 Fabrice Bellard.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17 5509bffa Diego Biurrun
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 dfdfa47c François Revol
 */
19
20
#include <signal.h>
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <unistd.h>
25
#include <sys/time.h>
26
27
#include <Application.h>
28
#include <SoundPlayer.h>
29
30
extern "C" {
31
#include "avformat.h"
32
}
33
34 788a2d3d François Revol
#ifdef HAVE_BSOUNDRECORDER
35
#include <SoundRecorder.h>
36 fb059921 François Revol
using namespace BPrivate::Media::Experimental;
37 788a2d3d François Revol
#endif
38
39 3810fbf5 François Revol
/* enable performance checks */
40
//#define PERF_CHECK
41 dfdfa47c François Revol
42 143d4644 François Revol
/* enable Media Kit latency checks */
43
//#define LATENCY_CHECK
44
45 dfdfa47c François Revol
#define AUDIO_BLOCK_SIZE 4096
46 3810fbf5 François Revol
#define AUDIO_BLOCK_COUNT 8
47
48
#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
49
50 dfdfa47c François Revol
typedef struct {
51 50515cdd François Revol
    int fd; // UNUSED
52 dfdfa47c François Revol
    int sample_rate;
53
    int channels;
54
    int frame_size; /* in bytes ! */
55
    CodecID codec_id;
56 d271b84b François Revol
    uint8_t buffer[AUDIO_BUFFER_SIZE];
57 dfdfa47c François Revol
    int buffer_ptr;
58 3810fbf5 François Revol
    /* ring buffer */
59
    sem_id input_sem;
60
    int input_index;
61
    sem_id output_sem;
62
    int output_index;
63 dfdfa47c François Revol
    BSoundPlayer *player;
64 788a2d3d François Revol
#ifdef HAVE_BSOUNDRECORDER
65
    BSoundRecorder *recorder;
66
#endif
67 dfdfa47c François Revol
    int has_quit; /* signal callbacks not to wait */
68 3810fbf5 François Revol
    volatile bigtime_t starve_time;
69 dfdfa47c François Revol
} AudioData;
70
71 3810fbf5 François Revol
static thread_id main_thid;
72
static thread_id bapp_thid;
73 dfdfa47c François Revol
static int own_BApp_created = 0;
74 3810fbf5 François Revol
static int refcount = 0;
75 dfdfa47c François Revol
76
/* create the BApplication and Run() it */
77
static int32 bapp_thread(void *arg)
78
{
79
    new BApplication("application/x-vnd.ffmpeg");
80
    own_BApp_created = 1;
81
    be_app->Run();
82
    /* kill the process group */
83 3810fbf5 François Revol
//    kill(0, SIGINT);
84
//    kill(main_thid, SIGHUP);
85 dfdfa47c François Revol
    return B_OK;
86
}
87
88 3810fbf5 François Revol
/* create the BApplication only if needed */
89
static void create_bapp_if_needed(void)
90
{
91
    if (refcount++ == 0) {
92
        /* needed by libmedia */
93
        if (be_app == NULL) {
94
            bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL);
95
            resume_thread(bapp_thid);
96
            while (!own_BApp_created)
97
                snooze(50000);
98
        }
99
    }
100
}
101
102
static void destroy_bapp_if_needed(void)
103
{
104
    if (--refcount == 0 && own_BApp_created) {
105
        be_app->Lock();
106
        be_app->Quit();
107
        be_app = NULL;
108
    }
109
}
110
111 dfdfa47c François Revol
/* called back by BSoundPlayer */
112
static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format)
113
{
114
    AudioData *s;
115 3810fbf5 François Revol
    size_t len, amount;
116 dfdfa47c François Revol
    unsigned char *buf = (unsigned char *)buffer;
117
118
    s = (AudioData *)cookie;
119
    if (s->has_quit)
120
        return;
121
    while (bufferSize > 0) {
122 3810fbf5 François Revol
#ifdef PERF_CHECK
123
        bigtime_t t;
124
        t = system_time();
125
#endif
126
        len = MIN(AUDIO_BLOCK_SIZE, bufferSize);
127
        if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
128
            s->has_quit = 1;
129 dfdfa47c François Revol
            s->player->SetHasData(false);
130
            return;
131
        }
132 3810fbf5 François Revol
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index));
133
        memcpy(buf, &s->buffer[s->output_index], amount);
134
        s->output_index += amount;
135
        if (s->output_index >= AUDIO_BUFFER_SIZE) {
136
            s->output_index %= AUDIO_BUFFER_SIZE;
137
            memcpy(buf + amount, &s->buffer[s->output_index], len - amount);
138
            s->output_index += len-amount;
139
            s->output_index %= AUDIO_BUFFER_SIZE;
140
        }
141
        release_sem_etc(s->input_sem, len, 0);
142
#ifdef PERF_CHECK
143
        t = system_time() - t;
144
        s->starve_time = MAX(s->starve_time, t);
145
#endif
146
        buf += len;
147
        bufferSize -= len;
148 dfdfa47c François Revol
    }
149
}
150
151 788a2d3d François Revol
#ifdef HAVE_BSOUNDRECORDER
152
/* called back by BSoundRecorder */
153
static void audiorecord_callback(void *cookie, bigtime_t timestamp, void *buffer, size_t bufferSize, const media_multi_audio_format &format)
154
{
155
    AudioData *s;
156
    size_t len, amount;
157
    unsigned char *buf = (unsigned char *)buffer;
158
159
    s = (AudioData *)cookie;
160
    if (s->has_quit)
161
        return;
162
163
    while (bufferSize > 0) {
164
        len = MIN(bufferSize, AUDIO_BLOCK_SIZE);
165
        //printf("acquire_sem(input, %d)\n", len);
166
        if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
167
            s->has_quit = 1;
168
            return;
169
        }
170
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index));
171
        memcpy(&s->buffer[s->input_index], buf, amount);
172
        s->input_index += amount;
173
        if (s->input_index >= AUDIO_BUFFER_SIZE) {
174
            s->input_index %= AUDIO_BUFFER_SIZE;
175
            memcpy(&s->buffer[s->input_index], buf + amount, len - amount);
176
            s->input_index += len - amount;
177
        }
178
        release_sem_etc(s->output_sem, len, 0);
179
        //printf("release_sem(output, %d)\n", len);
180
        buf += len;
181
        bufferSize -= len;
182
    }
183
}
184
#endif
185
186 335338e8 François Revol
static int audio_open(AudioData *s, int is_output, const char *audio_device)
187 dfdfa47c François Revol
{
188
    int p[2];
189
    int ret;
190
    media_raw_audio_format format;
191 788a2d3d François Revol
    media_multi_audio_format iformat;
192 dfdfa47c François Revol
193 788a2d3d François Revol
#ifndef HAVE_BSOUNDRECORDER
194 dfdfa47c François Revol
    if (!is_output)
195
        return -EIO; /* not for now */
196 788a2d3d François Revol
#endif
197 3810fbf5 François Revol
    s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input");
198
    if (s->input_sem < B_OK)
199
        return -EIO;
200
    s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output");
201
    if (s->output_sem < B_OK) {
202
        delete_sem(s->input_sem);
203
        return -EIO;
204
    }
205
    s->input_index = 0;
206
    s->output_index = 0;
207
    create_bapp_if_needed();
208 dfdfa47c François Revol
    s->frame_size = AUDIO_BLOCK_SIZE;
209 788a2d3d François Revol
    /* bump up the priority (avoid realtime though) */
210
    set_thread_priority(find_thread(NULL), B_DISPLAY_PRIORITY+1);
211
#ifdef HAVE_BSOUNDRECORDER
212
    if (!is_output) {
213 335338e8 François Revol
        bool wait_for_input = false;
214
        if (audio_device && !strcmp(audio_device, "wait:"))
215
            wait_for_input = true;
216
        s->recorder = new BSoundRecorder(&iformat, wait_for_input, "ffmpeg input", audiorecord_callback);
217
        if (wait_for_input && (s->recorder->InitCheck() == B_OK)) {
218
            s->recorder->WaitForIncomingConnection(&iformat);
219
        }
220 788a2d3d François Revol
        if (s->recorder->InitCheck() != B_OK || iformat.format != media_raw_audio_format::B_AUDIO_SHORT) {
221
            delete s->recorder;
222
            s->recorder = NULL;
223
            if (s->input_sem)
224
                delete_sem(s->input_sem);
225
            if (s->output_sem)
226
                delete_sem(s->output_sem);
227
            return -EIO;
228
        }
229
        s->codec_id = (iformat.byte_order == B_MEDIA_LITTLE_ENDIAN)?CODEC_ID_PCM_S16LE:CODEC_ID_PCM_S16BE;
230
        s->channels = iformat.channel_count;
231
        s->sample_rate = (int)iformat.frame_rate;
232
        s->frame_size = iformat.buffer_size;
233
        s->recorder->SetCookie(s);
234
        s->recorder->SetVolume(1.0);
235
        s->recorder->Start();
236
        return 0;
237
    }
238
#endif
239 dfdfa47c François Revol
    format = media_raw_audio_format::wildcard;
240
    format.format = media_raw_audio_format::B_AUDIO_SHORT;
241
    format.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN;
242
    format.channel_count = s->channels;
243
    format.buffer_size = s->frame_size;
244
    format.frame_rate = s->sample_rate;
245
    s->player = new BSoundPlayer(&format, "ffmpeg output", audioplay_callback);
246
    if (s->player->InitCheck() != B_OK) {
247
        delete s->player;
248
        s->player = NULL;
249 788a2d3d François Revol
        if (s->input_sem)
250
            delete_sem(s->input_sem);
251
        if (s->output_sem)
252
            delete_sem(s->output_sem);
253 dfdfa47c François Revol
        return -EIO;
254
    }
255
    s->player->SetCookie(s);
256
    s->player->SetVolume(1.0);
257
    s->player->Start();
258
    s->player->SetHasData(true);
259
    return 0;
260
}
261
262
static int audio_close(AudioData *s)
263
{
264 3810fbf5 François Revol
    if (s->input_sem)
265
        delete_sem(s->input_sem);
266
    if (s->output_sem)
267
        delete_sem(s->output_sem);
268 dfdfa47c François Revol
    s->has_quit = 1;
269
    if (s->player) {
270
        s->player->Stop();
271
    }
272
    if (s->player)
273
        delete s->player;
274 788a2d3d François Revol
#ifdef HAVE_BSOUNDRECORDER
275
    if (s->recorder)
276
        delete s->recorder;
277
#endif
278 3810fbf5 François Revol
    destroy_bapp_if_needed();
279 dfdfa47c François Revol
    return 0;
280
}
281
282
/* sound output support */
283
static int audio_write_header(AVFormatContext *s1)
284
{
285
    AudioData *s = (AudioData *)s1->priv_data;
286
    AVStream *st;
287
    int ret;
288
289
    st = s1->streams[0];
290 9f747cc3 Sam Hocevar
    s->sample_rate = st->codec->sample_rate;
291
    s->channels = st->codec->channels;
292 335338e8 François Revol
    ret = audio_open(s, 1, NULL);
293 dfdfa47c François Revol
    if (ret < 0)
294
        return -EIO;
295
    return 0;
296
}
297
298
static int audio_write_packet(AVFormatContext *s1, int stream_index,
299 a018d318 François Revol
                              const uint8_t *buf, int size, int64_t force_pts)
300 dfdfa47c François Revol
{
301
    AudioData *s = (AudioData *)s1->priv_data;
302
    int len, ret;
303 143d4644 François Revol
#ifdef LATENCY_CHECK
304
bigtime_t lat1, lat2;
305
lat1 = s->player->Latency();
306
#endif
307 3810fbf5 François Revol
#ifdef PERF_CHECK
308
    bigtime_t t = s->starve_time;
309
    s->starve_time = 0;
310
    printf("starve_time: %lld    \n", t);
311
#endif
312
    while (size > 0) {
313
        int amount;
314
        len = MIN(size, AUDIO_BLOCK_SIZE);
315
        if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK)
316
            return -EIO;
317
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index));
318
        memcpy(&s->buffer[s->input_index], buf, amount);
319
        s->input_index += amount;
320
        if (s->input_index >= AUDIO_BUFFER_SIZE) {
321
            s->input_index %= AUDIO_BUFFER_SIZE;
322
            memcpy(&s->buffer[s->input_index], buf + amount, len - amount);
323
            s->input_index += len - amount;
324
        }
325
        release_sem_etc(s->output_sem, len, 0);
326
        buf += len;
327
        size -= len;
328
    }
329 143d4644 François Revol
#ifdef LATENCY_CHECK
330
lat2 = s->player->Latency();
331
printf("#### BSoundPlayer::Latency(): before= %lld, after= %lld\n", lat1, lat2);
332
#endif
333 dfdfa47c François Revol
    return 0;
334
}
335
336
static int audio_write_trailer(AVFormatContext *s1)
337
{
338
    AudioData *s = (AudioData *)s1->priv_data;
339
340
    audio_close(s);
341
    return 0;
342
}
343
344
/* grab support */
345
346
static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
347
{
348
    AudioData *s = (AudioData *)s1->priv_data;
349
    AVStream *st;
350
    int ret;
351
352
    if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
353
        return -1;
354
355
    st = av_new_stream(s1, 0);
356
    if (!st) {
357
        return -ENOMEM;
358
    }
359
    s->sample_rate = ap->sample_rate;
360
    s->channels = ap->channels;
361
362 335338e8 François Revol
    ret = audio_open(s, 0, ap->device);
363 dfdfa47c François Revol
    if (ret < 0) {
364
        av_free(st);
365
        return -EIO;
366
    }
367 788a2d3d François Revol
    /* take real parameters */
368 9f747cc3 Sam Hocevar
    st->codec->codec_type = CODEC_TYPE_AUDIO;
369
    st->codec->codec_id = s->codec_id;
370
    st->codec->sample_rate = s->sample_rate;
371
    st->codec->channels = s->channels;
372 788a2d3d François Revol
    return 0;
373
    av_set_pts_info(s1, 48, 1, 1000000);  /* 48 bits pts in us */
374 dfdfa47c François Revol
}
375
376
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
377
{
378
    AudioData *s = (AudioData *)s1->priv_data;
379 788a2d3d François Revol
    int size;
380
    size_t len, amount;
381
    unsigned char *buf;
382
    status_t err;
383 dfdfa47c François Revol
384
    if (av_new_packet(pkt, s->frame_size) < 0)
385
        return -EIO;
386 788a2d3d François Revol
    buf = (unsigned char *)pkt->data;
387
    size = pkt->size;
388
    while (size > 0) {
389
        len = MIN(AUDIO_BLOCK_SIZE, size);
390
        //printf("acquire_sem(output, %d)\n", len);
391
        while ((err=acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL)) == B_INTERRUPTED);
392
        if (err < B_OK) {
393 dfdfa47c François Revol
            av_free_packet(pkt);
394
            return -EIO;
395
        }
396 788a2d3d François Revol
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index));
397
        memcpy(buf, &s->buffer[s->output_index], amount);
398
        s->output_index += amount;
399
        if (s->output_index >= AUDIO_BUFFER_SIZE) {
400
            s->output_index %= AUDIO_BUFFER_SIZE;
401
            memcpy(buf + amount, &s->buffer[s->output_index], len - amount);
402
            s->output_index += len-amount;
403
            s->output_index %= AUDIO_BUFFER_SIZE;
404
        }
405
        release_sem_etc(s->input_sem, len, 0);
406
        //printf("release_sem(input, %d)\n", len);
407
        buf += len;
408
        size -= len;
409 dfdfa47c François Revol
    }
410 788a2d3d François Revol
    //XXX: add pts info
411 dfdfa47c François Revol
    return 0;
412
}
413
414
static int audio_read_close(AVFormatContext *s1)
415
{
416
    AudioData *s = (AudioData *)s1->priv_data;
417
418
    audio_close(s);
419
    return 0;
420
}
421
422 788a2d3d François Revol
static AVInputFormat audio_in_format = {
423 dfdfa47c François Revol
    "audio_device",
424
    "audio grab and output",
425
    sizeof(AudioData),
426
    NULL,
427
    audio_read_header,
428
    audio_read_packet,
429
    audio_read_close,
430
    NULL,
431
    AVFMT_NOFILE,
432
};
433
434
AVOutputFormat audio_out_format = {
435
    "audio_device",
436
    "audio grab and output",
437
    "",
438
    "",
439
    sizeof(AudioData),
440
#ifdef WORDS_BIGENDIAN
441
    CODEC_ID_PCM_S16BE,
442
#else
443
    CODEC_ID_PCM_S16LE,
444
#endif
445
    CODEC_ID_NONE,
446
    audio_write_header,
447
    audio_write_packet,
448
    audio_write_trailer,
449
    AVFMT_NOFILE,
450
};
451
452
extern "C" {
453
454
int audio_init(void)
455
{
456 3810fbf5 François Revol
    main_thid = find_thread(NULL);
457 dfdfa47c François Revol
    av_register_input_format(&audio_in_format);
458
    av_register_output_format(&audio_out_format);
459
    return 0;
460
}
461
462
} // "C"