Statistics
| Branch: | Revision:

ffmpeg / libavformat / beosaudio.cpp @ 9870a7bd

History | View | Annotate | Download (12.8 KB)

1
/*
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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
 */
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
#ifdef HAVE_BSOUNDRECORDER
35
#include <SoundRecorder.h>
36
using namespace BPrivate::Media::Experimental;
37
#endif
38

    
39
/* enable performance checks */
40
//#define PERF_CHECK
41

    
42
/* enable Media Kit latency checks */
43
//#define LATENCY_CHECK
44

    
45
#define AUDIO_BLOCK_SIZE 4096
46
#define AUDIO_BLOCK_COUNT 8
47

    
48
#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
49

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

    
71
static thread_id main_thid;
72
static thread_id bapp_thid;
73
static int own_BApp_created = 0;
74
static int refcount = 0;
75

    
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
//    kill(0, SIGINT);
84
//    kill(main_thid, SIGHUP);
85
    return B_OK;
86
}
87

    
88
/* 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
/* 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
    size_t len, amount;
116
    unsigned char *buf = (unsigned char *)buffer;
117

    
118
    s = (AudioData *)cookie;
119
    if (s->has_quit)
120
        return;
121
    while (bufferSize > 0) {
122
#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
            s->player->SetHasData(false);
130
            return;
131
        }
132
        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
    }
149
}
150

    
151
#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
static int audio_open(AudioData *s, int is_output, const char *audio_device)
187
{
188
    int p[2];
189
    int ret;
190
    media_raw_audio_format format;
191
    media_multi_audio_format iformat;
192

    
193
#ifndef HAVE_BSOUNDRECORDER
194
    if (!is_output)
195
        return -EIO; /* not for now */
196
#endif
197
    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
    s->frame_size = AUDIO_BLOCK_SIZE;
209
    /* 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
        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
        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
    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
        if (s->input_sem)
250
            delete_sem(s->input_sem);
251
        if (s->output_sem)
252
            delete_sem(s->output_sem);
253
        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
    if (s->input_sem)
265
        delete_sem(s->input_sem);
266
    if (s->output_sem)
267
        delete_sem(s->output_sem);
268
    s->has_quit = 1;
269
    if (s->player) {
270
        s->player->Stop();
271
    }
272
    if (s->player)
273
        delete s->player;
274
#ifdef HAVE_BSOUNDRECORDER
275
    if (s->recorder)
276
        delete s->recorder;
277
#endif
278
    destroy_bapp_if_needed();
279
    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
    s->sample_rate = st->codec->sample_rate;
291
    s->channels = st->codec->channels;
292
    ret = audio_open(s, 1, NULL);
293
    if (ret < 0)
294
        return -EIO;
295
    return 0;
296
}
297

    
298
static int audio_write_packet(AVFormatContext *s1, int stream_index,
299
                              const uint8_t *buf, int size, int64_t force_pts)
300
{
301
    AudioData *s = (AudioData *)s1->priv_data;
302
    int len, ret;
303
#ifdef LATENCY_CHECK
304
bigtime_t lat1, lat2;
305
lat1 = s->player->Latency();
306
#endif
307
#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
#ifdef LATENCY_CHECK
330
lat2 = s->player->Latency();
331
printf("#### BSoundPlayer::Latency(): before= %lld, after= %lld\n", lat1, lat2);
332
#endif
333
    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
    ret = audio_open(s, 0, ap->device);
363
    if (ret < 0) {
364
        av_free(st);
365
        return -EIO;
366
    }
367
    /* take real parameters */
368
    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
    return 0;
373
    av_set_pts_info(s1, 48, 1, 1000000);  /* 48 bits pts in us */
374
}
375

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

    
384
    if (av_new_packet(pkt, s->frame_size) < 0)
385
        return -EIO;
386
    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
            av_free_packet(pkt);
394
            return -EIO;
395
        }
396
        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
    }
410
    //XXX: add pts info
411
    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
static AVInputFormat audio_in_format = {
423
    "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
    main_thid = find_thread(NULL);
457
    av_register_input_format(&audio_in_format);
458
    av_register_output_format(&audio_out_format);
459
    return 0;
460
}
461

    
462
} // "C"
463