Statistics
| Branch: | Revision:

ffmpeg / libavdevice / beosaudio.cpp @ 9c5a9e6b

History | View | Annotate | Download (13 KB)

1
/*
2
 * BeOS audio play interface
3
 * Copyright (c) 2000, 2001 Fabrice Bellard
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21

    
22
#include <signal.h>
23
#include <stdlib.h>
24
#include <stdio.h>
25
#include <string.h>
26
#include <unistd.h>
27
#include <sys/time.h>
28

    
29
#include <Application.h>
30
#include <SoundPlayer.h>
31

    
32
extern "C" {
33
#include "libavformat/avformat.h"
34
}
35

    
36
#if HAVE_BSOUNDRECORDER
37
#include <SoundRecorder.h>
38
using namespace BPrivate::Media::Experimental;
39
#endif
40

    
41
/* enable performance checks */
42
//#define PERF_CHECK
43

    
44
/* enable Media Kit latency checks */
45
//#define LATENCY_CHECK
46

    
47
#define AUDIO_BLOCK_SIZE 4096
48
#define AUDIO_BLOCK_COUNT 8
49

    
50
#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
51

    
52
typedef struct {
53
    int fd; // UNUSED
54
    int sample_rate;
55
    int channels;
56
    int frame_size; /* in bytes ! */
57
    CodecID codec_id;
58
    uint8_t buffer[AUDIO_BUFFER_SIZE];
59
    int buffer_ptr;
60
    /* ring buffer */
61
    sem_id input_sem;
62
    int input_index;
63
    sem_id output_sem;
64
    int output_index;
65
    BSoundPlayer *player;
66
#if HAVE_BSOUNDRECORDER
67
    BSoundRecorder *recorder;
68
#endif
69
    int has_quit; /* signal callbacks not to wait */
70
    volatile bigtime_t starve_time;
71
} AudioData;
72

    
73
static thread_id main_thid;
74
static thread_id bapp_thid;
75
static int own_BApp_created = 0;
76
static int refcount = 0;
77

    
78
/* create the BApplication and Run() it */
79
static int32 bapp_thread(void *arg)
80
{
81
    new BApplication("application/x-vnd.ffmpeg");
82
    own_BApp_created = 1;
83
    be_app->Run();
84
    /* kill the process group */
85
//    kill(0, SIGINT);
86
//    kill(main_thid, SIGHUP);
87
    return B_OK;
88
}
89

    
90
/* create the BApplication only if needed */
91
static void create_bapp_if_needed(void)
92
{
93
    if (refcount++ == 0) {
94
        /* needed by libmedia */
95
        if (be_app == NULL) {
96
            bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL);
97
            resume_thread(bapp_thid);
98
            while (!own_BApp_created)
99
                snooze(50000);
100
        }
101
    }
102
}
103

    
104
static void destroy_bapp_if_needed(void)
105
{
106
    if (--refcount == 0 && own_BApp_created) {
107
        be_app->Lock();
108
        be_app->Quit();
109
        be_app = NULL;
110
    }
111
}
112

    
113
/* called back by BSoundPlayer */
114
static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format)
115
{
116
    AudioData *s;
117
    size_t len, amount;
118
    unsigned char *buf = (unsigned char *)buffer;
119

    
120
    s = (AudioData *)cookie;
121
    if (s->has_quit)
122
        return;
123
    while (bufferSize > 0) {
124
#ifdef PERF_CHECK
125
        bigtime_t t;
126
        t = system_time();
127
#endif
128
        len = MIN(AUDIO_BLOCK_SIZE, bufferSize);
129
        if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
130
            s->has_quit = 1;
131
            s->player->SetHasData(false);
132
            return;
133
        }
134
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index));
135
        memcpy(buf, &s->buffer[s->output_index], amount);
136
        s->output_index += amount;
137
        if (s->output_index >= AUDIO_BUFFER_SIZE) {
138
            s->output_index %= AUDIO_BUFFER_SIZE;
139
            memcpy(buf + amount, &s->buffer[s->output_index], len - amount);
140
            s->output_index += len-amount;
141
            s->output_index %= AUDIO_BUFFER_SIZE;
142
        }
143
        release_sem_etc(s->input_sem, len, 0);
144
#ifdef PERF_CHECK
145
        t = system_time() - t;
146
        s->starve_time = MAX(s->starve_time, t);
147
#endif
148
        buf += len;
149
        bufferSize -= len;
150
    }
151
}
152

    
153
#if HAVE_BSOUNDRECORDER
154
/* called back by BSoundRecorder */
155
static void audiorecord_callback(void *cookie, bigtime_t timestamp, void *buffer, size_t bufferSize, const media_multi_audio_format &format)
156
{
157
    AudioData *s;
158
    size_t len, amount;
159
    unsigned char *buf = (unsigned char *)buffer;
160

    
161
    s = (AudioData *)cookie;
162
    if (s->has_quit)
163
        return;
164

    
165
    while (bufferSize > 0) {
166
        len = MIN(bufferSize, AUDIO_BLOCK_SIZE);
167
        //printf("acquire_sem(input, %d)\n", len);
168
        if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
169
            s->has_quit = 1;
170
            return;
171
        }
172
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index));
173
        memcpy(&s->buffer[s->input_index], buf, amount);
174
        s->input_index += amount;
175
        if (s->input_index >= AUDIO_BUFFER_SIZE) {
176
            s->input_index %= AUDIO_BUFFER_SIZE;
177
            memcpy(&s->buffer[s->input_index], buf + amount, len - amount);
178
            s->input_index += len - amount;
179
        }
180
        release_sem_etc(s->output_sem, len, 0);
181
        //printf("release_sem(output, %d)\n", len);
182
        buf += len;
183
        bufferSize -= len;
184
    }
185
}
186
#endif
187

    
188
static int audio_open(AudioData *s, int is_output, const char *audio_device)
189
{
190
    int p[2];
191
    int ret;
192
    media_raw_audio_format format;
193
    media_multi_audio_format iformat;
194

    
195
#if !HAVE_BSOUNDRECORDER
196
    if (!is_output)
197
        return AVERROR(EIO); /* not for now */
198
#endif
199
    s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input");
200
    if (s->input_sem < B_OK)
201
        return AVERROR(EIO);
202
    s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output");
203
    if (s->output_sem < B_OK) {
204
        delete_sem(s->input_sem);
205
        return AVERROR(EIO);
206
    }
207
    s->input_index = 0;
208
    s->output_index = 0;
209
    create_bapp_if_needed();
210
    s->frame_size = AUDIO_BLOCK_SIZE;
211
    /* bump up the priority (avoid realtime though) */
212
    set_thread_priority(find_thread(NULL), B_DISPLAY_PRIORITY+1);
213
#if HAVE_BSOUNDRECORDER
214
    if (!is_output) {
215
        bool wait_for_input = false;
216
        if (audio_device && !strcmp(audio_device, "wait:"))
217
            wait_for_input = true;
218
        s->recorder = new BSoundRecorder(&iformat, wait_for_input, "ffmpeg input", audiorecord_callback);
219
        if (wait_for_input && (s->recorder->InitCheck() == B_OK)) {
220
            s->recorder->WaitForIncomingConnection(&iformat);
221
        }
222
        if (s->recorder->InitCheck() != B_OK || iformat.format != media_raw_audio_format::B_AUDIO_SHORT) {
223
            delete s->recorder;
224
            s->recorder = NULL;
225
            if (s->input_sem)
226
                delete_sem(s->input_sem);
227
            if (s->output_sem)
228
                delete_sem(s->output_sem);
229
            return AVERROR(EIO);
230
        }
231
        s->codec_id = (iformat.byte_order == B_MEDIA_LITTLE_ENDIAN)?CODEC_ID_PCM_S16LE:CODEC_ID_PCM_S16BE;
232
        s->channels = iformat.channel_count;
233
        s->sample_rate = (int)iformat.frame_rate;
234
        s->frame_size = iformat.buffer_size;
235
        s->recorder->SetCookie(s);
236
        s->recorder->SetVolume(1.0);
237
        s->recorder->Start();
238
        return 0;
239
    }
240
#endif
241
    format = media_raw_audio_format::wildcard;
242
    format.format = media_raw_audio_format::B_AUDIO_SHORT;
243
    format.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN;
244
    format.channel_count = s->channels;
245
    format.buffer_size = s->frame_size;
246
    format.frame_rate = s->sample_rate;
247
    s->player = new BSoundPlayer(&format, "ffmpeg output", audioplay_callback);
248
    if (s->player->InitCheck() != B_OK) {
249
        delete s->player;
250
        s->player = NULL;
251
        if (s->input_sem)
252
            delete_sem(s->input_sem);
253
        if (s->output_sem)
254
            delete_sem(s->output_sem);
255
        return AVERROR(EIO);
256
    }
257
    s->player->SetCookie(s);
258
    s->player->SetVolume(1.0);
259
    s->player->Start();
260
    s->player->SetHasData(true);
261
    return 0;
262
}
263

    
264
static int audio_close(AudioData *s)
265
{
266
    if (s->input_sem)
267
        delete_sem(s->input_sem);
268
    if (s->output_sem)
269
        delete_sem(s->output_sem);
270
    s->has_quit = 1;
271
    if (s->player) {
272
        s->player->Stop();
273
    }
274
    if (s->player)
275
        delete s->player;
276
#if HAVE_BSOUNDRECORDER
277
    if (s->recorder)
278
        delete s->recorder;
279
#endif
280
    destroy_bapp_if_needed();
281
    return 0;
282
}
283

    
284
/* sound output support */
285
static int audio_write_header(AVFormatContext *s1)
286
{
287
    AudioData *s = (AudioData *)s1->priv_data;
288
    AVStream *st;
289
    int ret;
290

    
291
    st = s1->streams[0];
292
    s->sample_rate = st->codec->sample_rate;
293
    s->channels = st->codec->channels;
294
    ret = audio_open(s, 1, NULL);
295
    if (ret < 0)
296
        return AVERROR(EIO);
297
    return 0;
298
}
299

    
300
static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt)
301
{
302
    AudioData *s = (AudioData *)s1->priv_data;
303
    int len, ret;
304
    const uint8_t *buf = pkt->data;
305
    int size = pkt->size;
306
#ifdef LATENCY_CHECK
307
bigtime_t lat1, lat2;
308
lat1 = s->player->Latency();
309
#endif
310
#ifdef PERF_CHECK
311
    bigtime_t t = s->starve_time;
312
    s->starve_time = 0;
313
    printf("starve_time: %lld    \n", t);
314
#endif
315
    while (size > 0) {
316
        int amount;
317
        len = MIN(size, AUDIO_BLOCK_SIZE);
318
        if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK)
319
            return AVERROR(EIO);
320
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index));
321
        memcpy(&s->buffer[s->input_index], buf, amount);
322
        s->input_index += amount;
323
        if (s->input_index >= AUDIO_BUFFER_SIZE) {
324
            s->input_index %= AUDIO_BUFFER_SIZE;
325
            memcpy(&s->buffer[s->input_index], buf + amount, len - amount);
326
            s->input_index += len - amount;
327
        }
328
        release_sem_etc(s->output_sem, len, 0);
329
        buf += len;
330
        size -= len;
331
    }
332
#ifdef LATENCY_CHECK
333
lat2 = s->player->Latency();
334
printf("#### BSoundPlayer::Latency(): before= %lld, after= %lld\n", lat1, lat2);
335
#endif
336
    return 0;
337
}
338

    
339
static int audio_write_trailer(AVFormatContext *s1)
340
{
341
    AudioData *s = (AudioData *)s1->priv_data;
342

    
343
    audio_close(s);
344
    return 0;
345
}
346

    
347
/* grab support */
348

    
349
static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
350
{
351
    AudioData *s = (AudioData *)s1->priv_data;
352
    AVStream *st;
353
    int ret;
354

    
355
    if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
356
        return -1;
357

    
358
    st = av_new_stream(s1, 0);
359
    if (!st) {
360
        return AVERROR(ENOMEM);
361
    }
362
    s->sample_rate = ap->sample_rate;
363
    s->channels = ap->channels;
364

    
365
    ret = audio_open(s, 0, s1->filename);
366
    if (ret < 0) {
367
        av_free(st);
368
        return AVERROR(EIO);
369
    }
370
    /* take real parameters */
371
    st->codec->codec_type = CODEC_TYPE_AUDIO;
372
    st->codec->codec_id = s->codec_id;
373
    st->codec->sample_rate = s->sample_rate;
374
    st->codec->channels = s->channels;
375
    return 0;
376
    av_set_pts_info(st, 48, 1, 1000000);  /* 48 bits pts in us */
377
}
378

    
379
static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
380
{
381
    AudioData *s = (AudioData *)s1->priv_data;
382
    int size;
383
    size_t len, amount;
384
    unsigned char *buf;
385
    status_t err;
386

    
387
    if (av_new_packet(pkt, s->frame_size) < 0)
388
        return AVERROR(EIO);
389
    buf = (unsigned char *)pkt->data;
390
    size = pkt->size;
391
    while (size > 0) {
392
        len = MIN(AUDIO_BLOCK_SIZE, size);
393
        //printf("acquire_sem(output, %d)\n", len);
394
        while ((err=acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL)) == B_INTERRUPTED);
395
        if (err < B_OK) {
396
            av_free_packet(pkt);
397
            return AVERROR(EIO);
398
        }
399
        amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index));
400
        memcpy(buf, &s->buffer[s->output_index], amount);
401
        s->output_index += amount;
402
        if (s->output_index >= AUDIO_BUFFER_SIZE) {
403
            s->output_index %= AUDIO_BUFFER_SIZE;
404
            memcpy(buf + amount, &s->buffer[s->output_index], len - amount);
405
            s->output_index += len-amount;
406
            s->output_index %= AUDIO_BUFFER_SIZE;
407
        }
408
        release_sem_etc(s->input_sem, len, 0);
409
        //printf("release_sem(input, %d)\n", len);
410
        buf += len;
411
        size -= len;
412
    }
413
    //XXX: add pts info
414
    return 0;
415
}
416

    
417
static int audio_read_close(AVFormatContext *s1)
418
{
419
    AudioData *s = (AudioData *)s1->priv_data;
420

    
421
    audio_close(s);
422
    return 0;
423
}
424

    
425
static AVInputFormat audio_beos_demuxer = {
426
    "audio_beos",
427
    NULL_IF_CONFIG_SMALL("audio grab and output"),
428
    sizeof(AudioData),
429
    NULL,
430
    audio_read_header,
431
    audio_read_packet,
432
    audio_read_close,
433
    NULL,
434
    NULL,
435
    AVFMT_NOFILE,
436
};
437

    
438
AVOutputFormat audio_beos_muxer = {
439
    "audio_beos",
440
    NULL_IF_CONFIG_SMALL("audio grab and output"),
441
    "",
442
    "",
443
    sizeof(AudioData),
444
#if HAVE_BIGENDIAN
445
    CODEC_ID_PCM_S16BE,
446
#else
447
    CODEC_ID_PCM_S16LE,
448
#endif
449
    CODEC_ID_NONE,
450
    audio_write_header,
451
    audio_write_packet,
452
    audio_write_trailer,
453
    AVFMT_NOFILE,
454
};
455

    
456
extern "C" {
457

    
458
int audio_init(void)
459
{
460
    main_thid = find_thread(NULL);
461
    av_register_input_format(&audio_beos_demuxer);
462
    av_register_output_format(&audio_beos_muxer);
463
    return 0;
464
}
465

    
466
} // "C"
467