source: mediastreamer2/src/msandroid.cpp @ 1437:cac296fa228d

Last change on this file since 1437:cac296fa228d was 1437:cac296fa228d, checked in by Nikita Kozlov <nikita@…>, 16 months ago

flush and release the stream before joining the other thread

File size: 20.4 KB
Line 
1/*
2 * msandroid.cpp -Android Media plugin for Linphone-
3 *
4 *
5 * Copyright (C) 2009  Belledonne Communications, Grenoble, France
6 *
7 *  This program is free software; you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation; either version 2 of the License, or
10 *  (at your option) any later version.
11 *
12 *  This program 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
15 *  GNU Library General Public License for more details.
16 *
17 *  You should have received a copy of the GNU General Public License
18 *  along with this program; if not, write to the Free Software
19 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22#include <sys/times.h>
23#include <sys/resource.h>
24#include "mediastreamer2/mssndcard.h"
25#include "mediastreamer2/msfilter.h"
26#include "mediastreamer2/msticker.h"
27#include "mediastreamer2/msjava.h"
28#include <jni.h>
29
30#define MAX_READ_ERROR 100
31
32static void sound_read_setup(MSFilter *f);
33
34static void set_high_prio(void){
35        int result=0;
36/*      struct sched_param param;
37        memset(&param,0,sizeof(param));
38        int policy=SCHED_OTHER;
39        param.sched_priority=sched_get_priority_max(policy);*/
40        if((result=setpriority(PRIO_PROCESS, gettid(), -19))) {
41                ms_warning("Set sched param failed with error code(%i)\n",result);
42        } else {
43                ms_error("msandroid thread priority set to max %x\n", -19);
44        }
45}
46/*
47 mediastreamer2 sound card functions
48 */
49
50void msandroid_sound_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent)
51{
52}
53
54int msandroid_sound_get_level(MSSndCard *card, MSSndCardMixerElem e)
55{
56        return 0;
57}
58
59void msandroid_sound_set_source(MSSndCard *card, MSSndCardCapture source)
60{
61}
62
63void msandroid_sound_init(MSSndCard *card){
64}
65
66void msandroid_sound_uninit(MSSndCard *card){
67}
68
69void msandroid_sound_detect(MSSndCardManager *m);
70MSSndCard *msandroid_sound_duplicate(MSSndCard *obj);
71
72MSFilter *msandroid_sound_read_new(MSSndCard *card);
73MSFilter *msandroid_sound_write_new(MSSndCard *card);
74
75MSSndCardDesc msandroid_sound_card_desc = {
76/*.driver_type=*/"ANDROID SND",
77/*.detect=*/ msandroid_sound_detect,
78/*.init=*/msandroid_sound_init,
79/*.set_level=*/msandroid_sound_set_level,
80/*.get_level=*/msandroid_sound_get_level,
81/*.set_capture=*/msandroid_sound_set_source,
82/*.set_control=*/NULL,
83/*.get_control=*/NULL,
84/*.create_reader=*/msandroid_sound_read_new,
85/*.create_writer=*/msandroid_sound_write_new,
86/*.uninit=*/msandroid_sound_uninit,
87/*.duplicate=*/msandroid_sound_duplicate
88};
89
90MSSndCard *msandroid_sound_duplicate(MSSndCard *obj){
91        MSSndCard *card=ms_snd_card_new(&msandroid_sound_card_desc);
92        card->name=ms_strdup(obj->name);
93        return card;
94}
95
96MSSndCard *msandroid_sound_card_new(){
97        MSSndCard *card=ms_snd_card_new(&msandroid_sound_card_desc);
98        card->name=ms_strdup("Android Sound card");
99        return card;
100}
101
102void msandroid_sound_detect(MSSndCardManager *m){
103        ms_debug("msandroid_sound_detect");
104        MSSndCard *card=msandroid_sound_card_new();
105        ms_snd_card_manager_add_card(m,card);
106}
107
108
109/*************filter commun functions*********/
110class msandroid_sound_data {
111public:
112        msandroid_sound_data() : bits(16),rate(8000),nchannels(1),started(false),thread_id(0){
113                ms_mutex_init(&mutex,NULL);
114        };
115        ~msandroid_sound_data() {
116                ms_mutex_destroy(&mutex);
117        }
118        unsigned int    bits;
119        unsigned int    rate;
120        unsigned int    nchannels;
121        bool                    started;
122        ms_thread_t     thread_id;
123        ms_mutex_t              mutex;
124        int     buff_size; /*buffer size in bytes*/
125};
126
127
128static int get_rate(MSFilter *f, void *data){
129        msandroid_sound_data *d=(msandroid_sound_data*)f->data;
130        *(int*)data=d->rate;
131        return 0;
132}
133
134
135static int set_nchannels(MSFilter *f, void *arg){
136        ms_debug("set_nchannels %d", *((int*)arg));
137        msandroid_sound_data *d=(msandroid_sound_data*)f->data;
138        d->nchannels=*(int*)arg;
139        return 0;
140}
141
142
143
144
145/***********************************read filter********************/
146static int set_read_rate(MSFilter *f, void *arg){
147        int proposed_rate = *((int*)arg);
148        ms_debug("set_rate %d",proposed_rate);
149        msandroid_sound_data *d=(msandroid_sound_data*)f->data;
150        d->rate=proposed_rate;
151        return 0;
152}
153
154static int get_latency(MSFilter *f, void *arg){
155        msandroid_sound_data *d=(msandroid_sound_data*)f->data;
156        if (!d->started){
157                sound_read_setup(f);
158                *((int*)arg)=(1000*d->buff_size)/(d->nchannels*2*d->rate);
159        }
160        return 0;
161}
162             
163
164MSFilterMethod msandroid_sound_read_methods[]={
165        {       MS_FILTER_SET_SAMPLE_RATE       , set_read_rate },
166        {       MS_FILTER_GET_SAMPLE_RATE       , get_rate      },
167        {       MS_FILTER_SET_NCHANNELS         , set_nchannels },
168        {       MS_FILTER_GET_LATENCY   , get_latency},
169        {       0                               , NULL          }
170};
171
172
173class msandroid_sound_read_data : public msandroid_sound_data{
174public:
175        msandroid_sound_read_data() : audio_record(0),audio_record_class(0),read_buff(0),read_chunk_size(0) {
176                ms_bufferizer_init(&rb);
177        }
178        ~msandroid_sound_read_data() {
179                ms_bufferizer_uninit (&rb);
180        }
181        jobject                 audio_record;
182        jclass                  audio_record_class;
183        jbyteArray              read_buff;
184        MSBufferizer            rb;
185        int                     read_chunk_size;
186};
187
188static void* msandroid_read_cb(msandroid_sound_read_data* d) {
189        mblk_t *m;
190        int nread;
191        int nfailread = 0; 
192        jmethodID read_id=0;
193        jmethodID record_id=0;
194        jbyte* buf;
195
196
197        set_high_prio();
198
199        JNIEnv *jni_env = ms_get_jni_env();
200
201        ms_debug("read thread cb");
202        record_id = jni_env->GetMethodID(d->audio_record_class,"startRecording", "()V");
203        if(record_id==0) {
204                ms_error("cannot find AudioRecord.startRecording() method");
205                goto end;
206        }
207        //start recording
208        jni_env->CallVoidMethod(d->audio_record,record_id);
209
210        // int read (byte[] audioData, int offsetInBytes, int sizeInBytes)
211        read_id = jni_env->GetMethodID(d->audio_record_class,"read", "([BII)I");
212        if(read_id==0) {
213                ms_error("cannot find AudioRecord.read() method");
214                goto end;
215        }
216
217        buf = jni_env->GetByteArrayElements(d->read_buff, 0);
218
219        while (d->started){ 
220                bzero(buf, d->read_chunk_size);
221                nread=jni_env->CallIntMethod(d->audio_record,read_id,d->read_buff,0, d->read_chunk_size);
222                if (nread <= 0 || nread != d->read_chunk_size) {
223                        ms_debug("read failed with %d", nread);
224                        if (nfailread++ == MAX_READ_ERROR) {
225                                ms_error(" max read error reached, closing input");
226                                d->started = false;
227                                break;
228                        }
229                        continue;
230                } else {
231                        nfailread = 0;
232                }
233                m = allocb(nread,0);
234                //jni_env->GetByteArrayRegion(d->read_buff, 0,nread, (jbyte*)m->b_wptr);
235                memcpy((jbyte*)m->b_wptr, buf, nread);
236        //      ms_error("%i octets read\n begin %x",nread, *m->b_wptr);
237                m->b_wptr += nread;
238                ms_mutex_lock(&d->mutex);
239                ms_bufferizer_put (&d->rb,m);
240                ms_mutex_unlock(&d->mutex);
241        };
242
243        goto end;
244        end: {
245        ms_debug("record thread finished");
246        ms_thread_exit(NULL);
247        return 0;
248        }
249}
250
251static void sound_read_setup(MSFilter *f){
252        ms_debug("andsnd_read_preprocess");
253        msandroid_sound_read_data *d=(msandroid_sound_read_data*)f->data;
254        jmethodID constructor_id=0;
255        jmethodID min_buff_size_id;
256        //jmethodID set_notification_period;
257        int rc;
258
259        JNIEnv *jni_env = ms_get_jni_env();
260        d->audio_record_class = (jclass)jni_env->NewGlobalRef(jni_env->FindClass("android/media/AudioRecord"));
261        if (d->audio_record_class == 0) {
262                ms_error("cannot find  android/media/AudioRecord\n");
263                return;
264        }
265
266        constructor_id = jni_env->GetMethodID(d->audio_record_class,"<init>", "(IIIII)V");
267        if (constructor_id == 0) {
268                ms_error("cannot find  AudioRecord (int audioSource, int sampleRateInHz, \
269                int channelConfig, int audioFormat, int bufferSizeInBytes)");
270                return;
271        }
272        min_buff_size_id = jni_env->GetStaticMethodID(d->audio_record_class,"getMinBufferSize", "(III)I");
273        if (min_buff_size_id == 0) {
274                ms_error("cannot find  AudioRecord.getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)");
275                return;
276        }
277        d->buff_size = jni_env->CallStaticIntMethod(d->audio_record_class,min_buff_size_id,d->rate,2/*CHANNEL_CONFIGURATION_MONO*/,2/*  ENCODING_PCM_16BIT */);
278        if (d->buff_size <= 4096) d->buff_size = 4096*3/2;
279        d->read_chunk_size = 0.02*(float)d->rate*2.0*(float)d->nchannels;
280
281
282        if (d->buff_size > 0) {
283                ms_message("Configuring recorder with [%i] bits  rate [%i] nchanels [%i] buff size [%i], chunk size [%i]"
284                                ,d->bits
285                                ,d->rate
286                                ,d->nchannels
287                                ,d->buff_size
288                                ,d->read_chunk_size);
289        } else {
290                ms_message("Cannot configure recorder with [%i] bits  rate [%i] nchanels [%i] buff size [%i] chunk size [%i]"
291                                ,d->bits
292                                ,d->rate
293                                ,d->nchannels
294                                ,d->buff_size
295                                ,d->read_chunk_size);
296                return;
297        }
298
299        d->read_buff = jni_env->NewByteArray(d->buff_size);
300        d->read_buff = (jbyteArray)jni_env->NewGlobalRef(d->read_buff);
301        if (d->read_buff == 0) {
302                ms_error("cannot instanciate read buff");
303                return;
304        }
305
306        d->audio_record =  jni_env->NewObject(d->audio_record_class
307                        ,constructor_id
308                        ,7/*MIC*/
309                        ,d->rate
310                        ,2/*CHANNEL_CONFIGURATION_MONO*/
311                        ,2/*  ENCODING_PCM_16BIT */
312                        ,d->buff_size);
313
314
315        jmethodID  state_method_id = jni_env->GetMethodID(d->audio_record_class,"getState", "()I");
316        int state = jni_env->CallIntMethod(d->audio_record, state_method_id);
317        if(state == 0){ /* STATE_UNINITIALIZED */
318               
319                jmethodID release_method_id = jni_env->GetMethodID(d->audio_record_class,"release", "()V");
320                jni_env->CallIntMethod(d->audio_record, release_method_id);
321                d->audio_record =  jni_env->NewObject(d->audio_record_class
322                                ,constructor_id
323                                ,1/*MIC*/
324                                ,d->rate
325                                ,2/*CHANNEL_CONFIGURATION_MONO*/
326                                ,2/*  ENCODING_PCM_16BIT */
327                                ,d->buff_size);
328
329                state = jni_env->CallIntMethod(d->audio_record, state_method_id);
330
331                if (state == 0){
332                        jni_env->CallIntMethod(d->audio_record, release_method_id);
333                        ms_error("cannot instanciate audio record");
334                        return;
335                }
336                ms_debug("audio record ok with dev %d bis", 1);
337
338        }else
339                ms_debug("audio record ok with dev %d", 7);
340
341
342
343        if (d->audio_record == 0) {
344                ms_error("cannot instanciate AudioRecord");
345                return;
346        }
347        d->audio_record = jni_env->NewGlobalRef(d->audio_record);
348
349        d->started=true;
350}
351
352static void sound_read_preprocess(MSFilter *f){
353        msandroid_sound_read_data *d=(msandroid_sound_read_data*)f->data;
354        ms_debug("andsnd_read_preprocess");
355        if (!d->started)
356                sound_read_setup(f);
357}
358
359static void sound_read_postprocess(MSFilter *f){
360        msandroid_sound_read_data *d=(msandroid_sound_read_data*)f->data;
361        jmethodID stop_id=0;
362        jmethodID release_id=0;
363
364        JNIEnv *jni_env = ms_get_jni_env();
365
366        d->started = false;
367        if (d->thread_id !=0) ms_thread_join(d->thread_id,0);
368
369        //stop recording
370        stop_id = jni_env->GetMethodID(d->audio_record_class,"stop", "()V");
371        if(stop_id==0) {
372                ms_error("cannot find AudioRecord.stop() method");
373                goto end;
374        }
375
376        if (d->audio_record) {
377                jni_env->CallVoidMethod(d->audio_record,stop_id);
378
379                //release recorder
380                release_id = jni_env->GetMethodID(d->audio_record_class,"release", "()V");
381                if(release_id==0) {
382                        ms_error("cannot find AudioRecord.release() method");
383                        goto end;
384                }
385                jni_env->CallVoidMethod(d->audio_record,release_id);
386        }
387        goto end;
388        end: {
389                if (d->audio_record) jni_env->DeleteGlobalRef(d->audio_record);
390                jni_env->DeleteGlobalRef(d->audio_record_class);
391                if (d->read_buff) jni_env->DeleteGlobalRef(d->read_buff);
392                return;
393        }
394}
395
396static void sound_read_process(MSFilter *f){
397        msandroid_sound_read_data *d=(msandroid_sound_read_data*)f->data;
398        int nbytes=0.02*(float)d->rate*2.0*(float)d->nchannels;
399
400        if (d->thread_id == 0) {
401                int rc = ms_thread_create(&d->thread_id, 0, (void*(*)(void*))msandroid_read_cb, d);
402                if (rc){
403                        ms_error("cannot create read thread return code  is [%i]", rc);
404                        d->started=false;
405                }
406        }
407
408        // output a buffer only every 2 ticks + alpha
409        if ((f->ticker->time % 20)==0 || (f->ticker->time % 510)==0){
410                int err;
411                mblk_t *om=allocb(nbytes,0);
412                ms_mutex_lock(&d->mutex);
413                err=ms_bufferizer_read(&d->rb,om->b_wptr,nbytes);
414                ms_mutex_unlock(&d->mutex);
415                if (err==nbytes){
416                        om->b_wptr+=nbytes;
417                        ms_queue_put(f->outputs[0],om);
418                }else freemsg(om);
419        }
420}
421
422
423static MSFilterDesc msandroid_sound_read_desc={
424/*.id=*/MS_FILTER_PLUGIN_ID,
425/*.name=*/"MSAndSoundRead",
426/*.text=*/N_("Sound capture filter for Android"),
427/*.category=*/MS_FILTER_OTHER,
428/*.enc_fmt*/NULL,
429/*.ninputs=*/0,
430/*.noutputs=*/1,
431/*.init*/NULL,
432/*.preprocess=*/sound_read_preprocess,
433/*.process=*/sound_read_process,
434/*.postprocess=*/sound_read_postprocess,
435/*.uninit*/NULL,
436/*.methods=*/msandroid_sound_read_methods
437};
438
439MSFilter *msandroid_sound_read_new(MSSndCard *card){
440        ms_debug("msandroid_sound_read_new");
441        MSFilter *f=ms_filter_new_from_desc(&msandroid_sound_read_desc);
442        f->data=new msandroid_sound_read_data();
443        return f;
444}
445
446MS_FILTER_DESC_EXPORT(msandroid_sound_read_desc)
447
448/***********************************write filter********************/
449static int set_write_rate(MSFilter *f, void *arg){
450#ifndef USE_HARDWARE_RATE
451        msandroid_sound_data *d=(msandroid_sound_data*)f->data;
452        int proposed_rate = *((int*)arg);
453        ms_debug("set_rate %d",proposed_rate);
454        d->rate=proposed_rate;
455        return 0;
456#else
457/*audioflingler resampling is really bad
458we prefer do resampling by ourselves if cpu allows it*/
459        return -1;
460#endif
461}
462
463MSFilterMethod msandroid_sound_write_methods[]={
464        {       MS_FILTER_SET_SAMPLE_RATE       , set_write_rate        },
465        {       MS_FILTER_GET_SAMPLE_RATE       , get_rate      },
466        {       MS_FILTER_SET_NCHANNELS         , set_nchannels },
467        {       0                               , NULL          }
468};
469
470
471class msandroid_sound_write_data : public msandroid_sound_data{
472public:
473        msandroid_sound_write_data() :audio_track_class(0),audio_track(0),write_chunk_size(0),writtenBytes(0),last_sample_date(0){
474                bufferizer = ms_bufferizer_new();
475                ms_cond_init(&cond,0);
476                JNIEnv *jni_env = ms_get_jni_env();
477                audio_track_class = (jclass)jni_env->NewGlobalRef(jni_env->FindClass("android/media/AudioTrack"));
478                if (audio_track_class == 0) {
479                        ms_error("cannot find  android/media/AudioTrack\n");
480                        return;
481                }
482                jmethodID hwrate_id = jni_env->GetStaticMethodID(audio_track_class,"getNativeOutputSampleRate", "(I)I");
483                if (hwrate_id == 0) {
484                        ms_error("cannot find  int AudioRecord.getNativeOutputSampleRate(int streamType)");
485                        return;
486                }
487                rate = jni_env->CallStaticIntMethod(audio_track_class,hwrate_id,0 /*STREAM_VOICE_CALL*/);
488                ms_message("Hardware sample rate is %i",rate);
489        };
490        ~msandroid_sound_write_data() {
491                ms_bufferizer_flush(bufferizer);
492                ms_bufferizer_destroy(bufferizer);
493                ms_cond_destroy(&cond);
494                if (audio_track_class!=0){
495                        JNIEnv *env = ms_get_jni_env();
496                        env->DeleteGlobalRef(audio_track_class);
497                }
498        }
499        jclass                  audio_track_class;
500        jobject                 audio_track;
501        MSBufferizer    *bufferizer;
502        ms_cond_t               cond;
503        int                     write_chunk_size;
504        unsigned int    writtenBytes;
505        unsigned long   last_sample_date;
506        bool sleeping;
507        unsigned int getWriteBuffSize() {
508                return buff_size;
509        }
510        int getWrittenFrames() {
511                return writtenBytes/(nchannels*(bits/8));
512        }
513};
514
515static void* msandroid_write_cb(msandroid_sound_write_data* d) {
516        jbyteArray              write_buff;
517        jmethodID               write_id=0;
518
519        set_high_prio();
520        int buff_size = d->getWriteBuffSize();
521        JNIEnv *jni_env = ms_get_jni_env();
522        ms_debug("write thread cb");
523
524        // int write  (byte[] audioData, int offsetInBytes, int sizeInBytes)
525        write_id = jni_env->GetMethodID(d->audio_track_class,"write", "([BII)I");
526        if(write_id==0) {
527                ms_error("cannot find AudioTrack.write() method");
528                goto end;
529        }
530        write_buff = jni_env->NewByteArray(buff_size);
531        uint8_t tmpBuff[buff_size];
532
533        ms_mutex_lock(&d->mutex);
534        ms_bufferizer_flush(d->bufferizer);
535        ms_mutex_unlock(&d->mutex);
536
537        while(d->started) {
538                int bufferizer_size;
539
540                ms_mutex_lock(&d->mutex);
541               
542                while((bufferizer_size = ms_bufferizer_get_avail(d->bufferizer)) >= d->write_chunk_size) {
543                        ms_bufferizer_read(d->bufferizer, tmpBuff, d->write_chunk_size);
544                        ms_mutex_unlock(&d->mutex);
545                        jni_env->SetByteArrayRegion(write_buff,0,d->write_chunk_size,(jbyte*)tmpBuff);
546                        int result = jni_env->CallIntMethod(d->audio_track,write_id,write_buff,0,d->write_chunk_size);
547                        d->writtenBytes+=result;
548                        if (result <= 0) {
549                                ms_error("write operation has failed [%i]",result);
550                        }
551                        ms_mutex_lock(&d->mutex);
552                }
553                if (d->started) {
554                        d->sleeping=true;
555                        ms_cond_wait(&d->cond,&d->mutex);
556                        d->sleeping=false;
557                }
558                ms_mutex_unlock(&d->mutex);
559        }
560
561
562        goto end;
563        end: {
564                ms_debug("finishing writer thread");
565                ms_thread_exit(NULL);
566                return 0;
567        }
568}
569
570void msandroid_sound_write_preprocess(MSFilter *f){
571        ms_debug("write preprocess started");
572        msandroid_sound_write_data *d=(msandroid_sound_write_data*)f->data;
573        jmethodID constructor_id=0;
574        jmethodID play_id=0;
575
576        jmethodID min_buff_size_id;
577
578        JNIEnv *jni_env = ms_get_jni_env();
579       
580        if (d->audio_track_class == 0) {
581                return;
582        }
583
584        constructor_id = jni_env->GetMethodID(d->audio_track_class,"<init>", "(IIIIII)V");
585        if (constructor_id == 0) {
586                ms_error("cannot find  AudioTrack(int streamType, int sampleRateInHz, \
587                int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)");
588                return;
589        }
590
591        min_buff_size_id = jni_env->GetStaticMethodID(d->audio_track_class,"getMinBufferSize", "(III)I");
592        if (min_buff_size_id == 0) {
593                ms_error("cannot find  AudioTrack.getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)");
594                return;
595        }
596        d->buff_size = jni_env->CallStaticIntMethod(d->audio_track_class,min_buff_size_id,d->rate,2/*CHANNEL_CONFIGURATION_MONO*/,2/*  ENCODING_PCM_16BIT */);
597        d->write_chunk_size= (d->rate*(d->bits/8)*d->nchannels)*0.02;
598
599        if (d->buff_size > 0) {
600                ms_message("Configuring player with [%i] bits  rate [%i] nchanels [%i] buff size [%i] chunk size [%i]"
601                                ,d->bits
602                                ,d->rate
603                                ,d->nchannels
604                                ,d->buff_size
605                                ,d->write_chunk_size);
606        } else {
607                ms_message("Cannot configure player with [%i] bits  rate [%i] nchanels [%i] buff size [%i] chunk size [%i]"
608                                ,d->bits
609                                ,d->rate
610                                ,d->nchannels
611                                ,d->buff_size
612                                ,d->write_chunk_size);
613                return;
614        }
615        d->audio_track =  jni_env->NewObject(d->audio_track_class
616                        ,constructor_id
617                        ,0/*STREAM_VOICE_CALL*/
618                        ,d->rate
619                        ,2/*CHANNEL_CONFIGURATION_MONO*/
620                        ,2/*  ENCODING_PCM_16BIT */
621                        ,d->buff_size
622                        ,1/*MODE_STREAM */);
623        d->audio_track = jni_env->NewGlobalRef(d->audio_track);
624        if (d->audio_track == 0) {
625                ms_error("cannot instanciate AudioTrack");
626                return;
627        }
628        play_id = jni_env->GetMethodID(d->audio_track_class,"play", "()V");
629        if(play_id==0) {
630                ms_error("cannot find AudioTrack.play() method");
631                return;
632        }
633
634        //start playing
635        jni_env->CallVoidMethod(d->audio_track,play_id);
636
637
638        d->started = true;
639}
640
641void msandroid_sound_write_postprocess(MSFilter *f){
642        msandroid_sound_write_data *d=(msandroid_sound_write_data*)f->data;
643        jmethodID flush_id=0;
644        jmethodID stop_id=0;
645        jmethodID release_id=0;
646        JNIEnv *jni_env = ms_get_jni_env();
647
648        d->started=false;
649        ms_mutex_lock(&d->mutex);
650        ms_cond_signal(&d->cond);
651        ms_mutex_unlock(&d->mutex);
652        //ms_thread_join(d->thread_id,0);
653        // flush
654        flush_id = jni_env->GetMethodID(d->audio_track_class,"flush", "()V");
655        if(flush_id==0) {
656                ms_error("cannot find AudioTrack.flush() method");
657                goto end;
658        }
659        if (d->audio_track) {
660
661                jni_env->CallVoidMethod(d->audio_track,flush_id);
662
663                //stop playing
664                stop_id = jni_env->GetMethodID(d->audio_track_class,"stop", "()V");
665                if(stop_id==0) {
666                        ms_error("cannot find AudioTrack.stop() method");
667                        goto end;
668                }
669                jni_env->CallVoidMethod(d->audio_track,stop_id);
670
671                //release playing
672                release_id = jni_env->GetMethodID(d->audio_track_class,"release", "()V");
673                if(release_id==0) {
674                        ms_error("cannot find AudioTrack.release() method");
675                        goto end;
676                }
677                jni_env->CallVoidMethod(d->audio_track,release_id);
678        }
679
680        goto end;
681end: {
682        if (d->audio_track) jni_env->DeleteGlobalRef(d->audio_track);
683        //d->jvm->DetachCurrentThread();
684        return;
685}
686
687}
688
689
690
691void msandroid_sound_write_process(MSFilter *f){
692        msandroid_sound_write_data *d=(msandroid_sound_write_data*)f->data;
693        mblk_t *m;
694       
695        if (d->thread_id == 0) {
696                int rc = ms_thread_create(&d->thread_id, 0, (void*(*)(void*))msandroid_write_cb, d);
697                if (rc){
698                        ms_error("cannot create write thread return code  is [%i]", rc);
699                        d->started = false;
700                        return;
701                }
702        }
703        while((m=ms_queue_get(f->inputs[0]))!=NULL){
704                if (d->started){
705                        ms_mutex_lock(&d->mutex);
706                        ms_bufferizer_put(d->bufferizer,m);
707                        if (d->sleeping)
708                                ms_cond_signal(&d->cond);
709                        d->last_sample_date=f->ticker->time;
710                        ms_mutex_unlock(&d->mutex);
711                }else freemsg(m);
712        }
713}
714
715
716static MSFilterDesc msandroid_sound_write_desc={
717/*.id=*/MS_FILTER_PLUGIN_ID,
718/*.name=*/"MSAndSoundWrite",
719/*.text=*/N_("Sound playback filter for Android"),
720/*.category=*/MS_FILTER_OTHER,
721/*.enc_fmt*/NULL,
722/*.ninputs=*/1,
723/*.noutputs=*/0,
724/*.init*/NULL,
725/*.preprocess=*/msandroid_sound_write_preprocess,
726/*.process=*/msandroid_sound_write_process,
727/*.postprocess=*/msandroid_sound_write_postprocess,
728/*.uninit*/NULL,
729/*.methods=*/msandroid_sound_write_methods
730};
731
732
733MSFilter *msandroid_sound_write_new(MSSndCard *card){
734        ms_debug("msandroid_sound_write_new");
735        MSFilter *f=ms_filter_new_from_desc(&msandroid_sound_write_desc);
736        f->data=new msandroid_sound_write_data();
737        return f;
738}
739
740
741MS_FILTER_DESC_EXPORT(msandroid_sound_write_desc)
742
Note: See TracBrowser for help on using the repository browser.