Statistics
| Branch: | Revision:

ffmpeg / libavformat / beosaudio.cpp @ 68bf8d83

History | View | Annotate | Download (12.9 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 "avformat.h"
34
}
35

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

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

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

    
346
/* grab support */
347

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

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

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

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

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

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

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

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

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

    
436
AVOutputFormat audio_muxer = {
437
    "audio_device",
438
    "audio grab and output",
439
    "",
440
    "",
441
    sizeof(AudioData),
442
#ifdef WORDS_BIGENDIAN
443
    CODEC_ID_PCM_S16BE,
444
#else
445
    CODEC_ID_PCM_S16LE,
446
#endif
447
    CODEC_ID_NONE,
448
    audio_write_header,
449
    audio_write_packet,
450
    audio_write_trailer,
451
    AVFMT_NOFILE,
452
};
453

    
454
extern "C" {
455

    
456
int audio_init(void)
457
{
458
    main_thid = find_thread(NULL);
459
    av_register_input_format(&audio_demuxer);
460
    av_register_output_format(&audio_muxer);
461
    return 0;
462
}
463

    
464
} // "C"
465