1 /******************************************************************************
4 Lantiq Deutschland GmbH
5 Am Campeon 3; 85579 Neubiberg, Germany
7 For licensing information, see the file 'LICENSE' in the root folder of
10 ******************************************************************************/
11 #include <pjmedia-audiodev/audiodev_imp.h>
12 #include <pjmedia/errno.h>
13 #include <pj/assert.h>
25 #include <sys/types.h>
26 #include <sys/ioctl.h>
27 #include <sys/select.h>
31 #if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE
34 #include "drv_tapi_io.h"
37 /* Maximum 2 devices*/
38 #define TAPI_AUDIO_DEV_NUM (1)
39 #define TAPI_AUDIO_MAX_DEV_NUM (2)
40 #define TAPI_BASE_NAME "TAPI"
41 #define TAPI_LL_DEV_BASE_PATH "/dev/vmmc"
42 #define TAPI_LL_DEV_FIRMWARE_NAME "/lib/firmware/danube_firmware.bin"
44 #define TAPI_LL_DEV_SELECT_TIMEOUT_MS (2000)
45 #define TAPI_LL_DEV_MAX_PACKET_SIZE (800)
46 #define TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE (12)
47 #define TAPI_LL_DEV_ENC_FRAME_LEN_MS (20)
48 #define TAPI_LL_DEV_ENC_SMPL_PER_SEC (8000)
49 #define TAPI_LL_DEV_ENC_BITS_PER_SMPLS (16)
50 #define TAPI_LL_DEV_ENC_SMPL_PER_FRAME (160)
51 #define TAPI_LL_DEV_ENC_BYTES_PER_FRAME (TAPI_LL_DEV_ENC_SMPL_PER_FRAME * (TAPI_LL_DEV_ENC_BITS_PER_SMPLS / 8))
54 #define FD_WIDTH_SET(fd, maxfd) (maxfd) < (fd) ? (fd) : maxfd
56 #define THIS_FILE "tapi_dev.c"
59 # define TRACE_(x) PJ_LOG(1,x)
67 pj_int32_t ch_fd
[TAPI_AUDIO_DEV_NUM
];
68 pj_int8_t data2phone_map
[TAPI_AUDIO_DEV_NUM
];
74 struct tapi_aud_factory
76 pjmedia_aud_dev_factory base
;
80 pj_uint32_t dev_count
;
81 pjmedia_aud_dev_info
*dev_info
;
86 typedef struct tapi_aud_factory tapi_aud_factory_t
;
89 Sound stream descriptor.
91 struct tapi_aud_stream
94 pjmedia_aud_stream base
; /**< Base class. */
96 pj_pool_t
*pool
; /**< Memory pool. */
98 pjmedia_aud_param param
; /**< Stream param. */
99 pjmedia_aud_rec_cb rec_cb
; /**< Record callback. */
100 pjmedia_aud_play_cb play_cb
; /**< Playback callback. */
101 void *user_data
; /**< Application data. */
103 pj_thread_desc thread_desc
;
107 pj_timestamp timestamp
;
110 typedef struct tapi_aud_stream tapi_aud_stream_t
;
112 /* Factory prototypes */
113 static pj_status_t
factory_init(pjmedia_aud_dev_factory
*f
);
114 static pj_status_t
factory_destroy(pjmedia_aud_dev_factory
*f
);
115 static unsigned factory_get_dev_count(pjmedia_aud_dev_factory
*f
);
116 static pj_status_t
factory_get_dev_info(pjmedia_aud_dev_factory
*f
,
118 pjmedia_aud_dev_info
*info
);
119 static pj_status_t
factory_default_param(pjmedia_aud_dev_factory
*f
,
121 pjmedia_aud_param
*param
);
122 static pj_status_t
factory_create_stream(pjmedia_aud_dev_factory
*f
,
123 const pjmedia_aud_param
*param
,
124 pjmedia_aud_rec_cb rec_cb
,
125 pjmedia_aud_play_cb play_cb
,
127 pjmedia_aud_stream
**p_aud_strm
);
129 /* Stream prototypes */
130 static pj_status_t
stream_get_param(pjmedia_aud_stream
*strm
,
131 pjmedia_aud_param
*param
);
132 static pj_status_t
stream_get_cap(pjmedia_aud_stream
*strm
,
133 pjmedia_aud_dev_cap cap
,
135 static pj_status_t
stream_set_cap(pjmedia_aud_stream
*strm
,
136 pjmedia_aud_dev_cap cap
,
138 static pj_status_t
stream_start(pjmedia_aud_stream
*strm
);
139 static pj_status_t
stream_stop(pjmedia_aud_stream
*strm
);
140 static pj_status_t
stream_destroy(pjmedia_aud_stream
*strm
);
142 static pjmedia_aud_dev_factory_op tapi_fact_op
=
146 &factory_get_dev_count
,
147 &factory_get_dev_info
,
148 &factory_default_param
,
149 &factory_create_stream
152 static pjmedia_aud_stream_op tapi_strm_op
=
162 void (*tapi_digit_callback
)(unsigned char digit
) = NULL
;
163 void (*tapi_hook_callback
)(unsigned char event
) = NULL
;
165 static pj_int32_t
tapi_dev_open(char* dev_path
, const pj_int32_t ch_num
)
167 char devname
[128] = {0};
169 pj_ansi_sprintf(devname
,"%s%u%u", dev_path
, 1, ch_num
);
171 return open((const char*)devname
, O_RDWR
, 0644);
174 static pj_status_t
tapi_dev_binary_buffer_create(
179 pj_status_t status
= PJ_SUCCESS
;
181 struct stat file_stat
;
183 /* Open binary file for reading*/
184 fd
= fopen(pPath
, "rb");
186 TRACE_((THIS_FILE
, "ERROR - binary file %s open failed!\n", pPath
));
190 /* Get file statistics*/
191 if (stat(pPath
, &file_stat
) != 0) {
192 TRACE_((THIS_FILE
, "ERROR - file %s statistics get failed!\n", pPath
));
196 *ppBuf
= malloc(file_stat
.st_size
);
197 if (*ppBuf
== NULL
) {
198 TRACE_((THIS_FILE
, "ERROR - binary file %s memory allocation failed!\n", pPath
));
199 status
= PJ_EUNKNOWN
;
204 if (fread (*ppBuf
, sizeof(pj_uint8_t
), file_stat
.st_size
, fd
) <= 0) {
205 TRACE_((THIS_FILE
, "ERROR - file %s read failed!\n", pPath
));
206 status
= PJ_EUNKNOWN
;
211 *pBufSz
= file_stat
.st_size
;
218 if (*ppBuf
!= NULL
&& status
!= PJ_SUCCESS
) {
225 static void tapi_dev_binary_buffer_delete(pj_uint8_t
*pBuf
)
231 static pj_status_t
tapi_dev_firmware_download(
235 pj_status_t status
= PJ_SUCCESS
;
236 pj_uint8_t
*pFirmware
= NULL
;
237 pj_uint32_t binSz
= 0;
238 VMMC_IO_INIT vmmc_io_init
;
240 /* Create binary buffer*/
241 status
= tapi_dev_binary_buffer_create(pPath
, &pFirmware
, &binSz
);
242 if (status
!= PJ_SUCCESS
) {
243 TRACE_((THIS_FILE
, "ERROR - binary buffer create failed!\n"));
248 /* Download Voice Firmware*/
249 memset(&vmmc_io_init
, 0, sizeof(VMMC_IO_INIT
));
250 vmmc_io_init
.pPRAMfw
= pFirmware
;
251 vmmc_io_init
.pram_size
= binSz
;
253 status
= ioctl(fd
, FIO_FW_DOWNLOAD
, &vmmc_io_init
);
254 if (status
!= PJ_SUCCESS
) {
255 TRACE_((THIS_FILE
, "ERROR - FIO_FW_DOWNLOAD ioctl failed!"));
258 /* Delete binary buffer*/
259 tapi_dev_binary_buffer_delete(pFirmware
);
265 static pj_status_t
tapi_dev_start(tapi_aud_factory_t
*f
)
267 pj_status_t status
= PJ_SUCCESS
;
269 IFX_TAPI_DEV_START_CFG_t tapistart
;
270 IFX_TAPI_MAP_DATA_t datamap
;
271 IFX_TAPI_ENC_CFG_t enc_cfg
;
272 IFX_TAPI_LINE_VOLUME_t vol
;
275 f
->dev_ctx
.dev_fd
= tapi_dev_open(TAPI_LL_DEV_BASE_PATH
, 0);
277 if (f
->dev_ctx
.dev_fd
< 0) {
278 TRACE_((THIS_FILE
, "ERROR - TAPI device open failed!"));
282 for (c
= 0; c
< TAPI_AUDIO_DEV_NUM
; c
++) {
283 f
->dev_ctx
.ch_fd
[c
] = tapi_dev_open(TAPI_LL_DEV_BASE_PATH
, TAPI_AUDIO_MAX_DEV_NUM
- c
);
285 if (f
->dev_ctx
.dev_fd
< 0) {
286 TRACE_((THIS_FILE
, "ERROR - TAPI channel%d open failed!", c
));
290 f
->dev_ctx
.data2phone_map
[c
] = c
& 0x1 ? 0 : 1;
293 status
= tapi_dev_firmware_download(f
->dev_ctx
.dev_fd
, TAPI_LL_DEV_FIRMWARE_NAME
);
294 if (status
!= PJ_SUCCESS
) {
295 TRACE_((THIS_FILE
, "ERROR - Voice Firmware Download failed!"));
299 memset(&tapistart
, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t
));
300 tapistart
.nMode
= IFX_TAPI_INIT_MODE_VOICE_CODER
;
303 status
= ioctl(f
->dev_ctx
.dev_fd
, IFX_TAPI_DEV_START
, &tapistart
);
304 if (status
!= PJ_SUCCESS
) {
305 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_DEV_START ioctl failed"));
310 for (c
= 0; c
< TAPI_AUDIO_DEV_NUM
; c
++) {
312 memset(&datamap
, 0x0, sizeof(IFX_TAPI_MAP_DATA_t
));
313 datamap
.nDstCh
= f
->dev_ctx
.data2phone_map
[c
];
314 datamap
.nChType
= IFX_TAPI_MAP_TYPE_PHONE
;
316 status
= ioctl(f
->dev_ctx
.ch_fd
[c
], IFX_TAPI_MAP_DATA_ADD
, &datamap
);
318 if (status
!= PJ_SUCCESS
) {
319 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_MAP_DATA_ADD ioctl failed"));
324 status
= ioctl(f
->dev_ctx
.ch_fd
[c
], IFX_TAPI_LINE_FEED_SET
, IFX_TAPI_LINE_FEED_STANDBY
);
326 if (status
!= PJ_SUCCESS
) {
327 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed"));
331 /* Config encoder for linear stream*/
332 memset(&enc_cfg
, 0x0, sizeof(IFX_TAPI_ENC_CFG_t
));
334 enc_cfg
.nFrameLen
= IFX_TAPI_COD_LENGTH_20
;
335 enc_cfg
.nEncType
= IFX_TAPI_COD_TYPE_LIN16_8
;
337 status
= ioctl(f
->dev_ctx
.ch_fd
[c
], IFX_TAPI_ENC_CFG_SET
, &enc_cfg
);
338 if (status
!= PJ_SUCCESS
) {
339 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_ENC_CFG_SET ioctl failed"));
344 /* Suppress TAPI volume, otherwise PJSIP starts autogeneration!!!*/
348 status
= ioctl(f
->dev_ctx
.ch_fd
[c
], IFX_TAPI_PHONE_VOLUME_SET
, &vol
);
349 if (status
!= PJ_SUCCESS
) {
350 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_PHONE_VOLUME_SET ioctl failed"));
359 static pj_status_t
tapi_dev_stop(tapi_aud_factory_t
*f
)
361 pj_status_t status
= PJ_SUCCESS
;
364 /* Stop TAPI device*/
365 if (ioctl(f
->dev_ctx
.dev_fd
, IFX_TAPI_DEV_STOP
, 0) != PJ_SUCCESS
) {
366 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_DEV_STOP ioctl failed"));
367 status
= PJ_EUNKNOWN
;
371 close(f
->dev_ctx
.dev_fd
);
373 /* Close channel FD*/
374 for (c
= TAPI_AUDIO_DEV_NUM
; c
> 0; c
--) {
375 close(f
->dev_ctx
.ch_fd
[TAPI_AUDIO_DEV_NUM
-c
]);
382 static pj_status_t
tapi_dev_codec_control(pj_int32_t fd
, pj_uint8_t start
)
384 if (ioctl(fd
, start
? IFX_TAPI_ENC_START
: IFX_TAPI_ENC_STOP
, 0) != PJ_SUCCESS
) {
385 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_ENC_%s ioctl failed!",
386 start
? "START" : "STOP"));
391 if (ioctl(fd
, start
? IFX_TAPI_DEC_START
: IFX_TAPI_DEC_STOP
, 0) != IFX_SUCCESS
) {
392 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_DEC_%s ioctl failed!",
393 start
? "START" : "STOP"));
401 static pj_status_t
tapi_dev_event_ONHOOK(tapi_ctx
*dev_ctx
, pj_uint32_t dev_idx
)
403 PJ_LOG(1,(THIS_FILE
, "TAPI: ONHOOK"));
405 if (ioctl(dev_ctx
->ch_fd
[dev_idx
], IFX_TAPI_LINE_FEED_SET
,
406 IFX_TAPI_LINE_FEED_STANDBY
) != PJ_SUCCESS
) {
407 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!"));
413 if (tapi_dev_codec_control(dev_ctx
->ch_fd
[dev_idx
], 0) != PJ_SUCCESS
) {
414 TRACE_((THIS_FILE
, "ERROR - codec start failed!"));
422 static pj_status_t
tapi_dev_event_OFFHOOK(tapi_ctx
*dev_ctx
, pj_uint32_t dev_idx
)
424 PJ_LOG(1,(THIS_FILE
, "TAPI: OFFHOOK"));
426 if (ioctl(dev_ctx
->ch_fd
[dev_idx
], IFX_TAPI_LINE_FEED_SET
,
427 IFX_TAPI_LINE_FEED_ACTIVE
) != PJ_SUCCESS
) {
428 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!"));
434 if (tapi_dev_codec_control(dev_ctx
->ch_fd
[dev_idx
], 1) != PJ_SUCCESS
) {
435 TRACE_((THIS_FILE
, "ERROR - codec start failed!"));
443 static pj_status_t
tapi_dev_event_digit(tapi_ctx
*dev_ctx
, pj_uint32_t dev_idx
)
445 PJ_LOG(1,(THIS_FILE
, "TAPI: OFFHOOK"));
447 if (ioctl(dev_ctx
->ch_fd
[dev_idx
], IFX_TAPI_LINE_FEED_SET
,
448 IFX_TAPI_LINE_FEED_ACTIVE
) != PJ_SUCCESS
) {
449 TRACE_((THIS_FILE
, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!"));
455 if (tapi_dev_codec_control(dev_ctx
->ch_fd
[dev_idx
], 1) != PJ_SUCCESS
) {
456 TRACE_((THIS_FILE
, "ERROR - codec start failed!"));
464 static pj_status_t
tapi_dev_event_handler(
465 tapi_aud_stream_t
*stream
)
467 tapi_ctx
*dev_ctx
= stream
->dev_ctx
;
468 pj_uint32_t dev_idx
= stream
->param
.rec_id
;
469 pj_status_t status
= PJ_SUCCESS
;
470 IFX_TAPI_EVENT_t tapiEvent
;
472 memset (&tapiEvent
, 0, sizeof(tapiEvent
));
474 tapiEvent
.ch
= dev_ctx
->data2phone_map
[dev_idx
];
477 status
= ioctl(dev_ctx
->dev_fd
, IFX_TAPI_EVENT_GET
, &tapiEvent
);
479 if ((status
== PJ_SUCCESS
) && (tapiEvent
.id
!= IFX_TAPI_EVENT_NONE
)) {
480 switch(tapiEvent
.id
) {
481 case IFX_TAPI_EVENT_FXS_ONHOOK
:
482 status
= tapi_dev_event_ONHOOK(dev_ctx
, dev_idx
);
483 if(tapi_hook_callback
)
484 tapi_hook_callback(0);
486 case IFX_TAPI_EVENT_FXS_OFFHOOK
:
487 status
= tapi_dev_event_OFFHOOK(dev_ctx
, dev_idx
);
488 if(tapi_hook_callback
)
489 tapi_hook_callback(1);
491 case IFX_TAPI_EVENT_DTMF_DIGIT
:
492 if(tapi_digit_callback
)
493 tapi_digit_callback(tapiEvent
.data
.dtmf
.ascii
);
496 printf("%s:%s[%d]%04X\n", __FILE__
, __func__
, __LINE__
, tapiEvent
.id
);
504 static pj_status_t
tapi_dev_data_handler(
505 tapi_aud_stream_t
*stream
)
507 pj_status_t status
= PJ_SUCCESS
;
508 tapi_ctx
*dev_ctx
= stream
->dev_ctx
;
509 pj_uint32_t dev_idx
= stream
->param
.rec_id
;
510 pj_uint8_t buf_rec
[TAPI_LL_DEV_ENC_BYTES_PER_FRAME
+ TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE
]={0};
511 pj_uint8_t buf_play
[TAPI_LL_DEV_ENC_BYTES_PER_FRAME
+ TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE
]={0};
512 pjmedia_frame frame_rec
, frame_play
;
515 /* Get data from driver*/
516 ret
= read(dev_ctx
->ch_fd
[dev_idx
], buf_rec
, sizeof(buf_rec
));
518 TRACE_((THIS_FILE
, "ERROR - no data available from device!"));
524 frame_rec
.type
= PJMEDIA_FRAME_TYPE_AUDIO
;
525 frame_rec
.buf
= buf_rec
+ TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE
;
526 frame_rec
.size
= ret
- TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE
;
527 frame_rec
.timestamp
.u64
= stream
->timestamp
.u64
;
529 status
= stream
->rec_cb(stream
->user_data
, &frame_rec
);
530 if (status
!= PJ_SUCCESS
)
532 PJ_LOG(1, (THIS_FILE
, "rec_cb() failed %d", status
));
535 frame_play
.type
= PJMEDIA_FRAME_TYPE_AUDIO
;
536 frame_play
.buf
= buf_play
+ TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE
;
537 frame_play
.size
= TAPI_LL_DEV_ENC_BYTES_PER_FRAME
;
538 frame_play
.timestamp
.u64
= stream
->timestamp
.u64
;
540 status
= (*stream
->play_cb
)(stream
->user_data
, &frame_play
);
541 if (status
!= PJ_SUCCESS
)
543 PJ_LOG(1, (THIS_FILE
, "play_cb() failed %d", status
));
547 memcpy(buf_play
, buf_rec
, TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE
);
549 ret
= write(dev_ctx
->ch_fd
[dev_idx
], buf_play
, sizeof(buf_play
));
552 PJ_LOG(1, (THIS_FILE
, "ERROR - device data writing failed!"));
557 PJ_LOG(1, (THIS_FILE
, "ERROR - no data written to device!"));
562 stream
->timestamp
.u64
+= TAPI_LL_DEV_ENC_SMPL_PER_FRAME
;
568 /* TAPI capture and playback thread. */
569 static int PJ_THREAD_FUNC
tapi_dev_thread(void *arg
)
571 tapi_aud_stream_t
*strm
= (struct tapi_aud_stream
*)arg
;
572 tapi_ctx
*dev_ctx
= strm
->dev_ctx
;
574 pj_uint32_t width
= 0;
579 PJ_LOG(1,(THIS_FILE
, "TAPI: thread starting..."));
581 if (strm
->param
.rec_id
!= strm
->param
.play_id
) {
582 PJ_LOG(1,(THIS_FILE
, "TAPI: thread exit - incorrect play/rec IDs"));
586 dev_idx
= strm
->param
.rec_id
;
590 FD_SET(dev_ctx
->dev_fd
, &rfds
);
591 width
= FD_WIDTH_SET(dev_ctx
->dev_fd
, width
);
593 FD_SET(dev_ctx
->ch_fd
[dev_idx
], &rfds
);
594 width
= FD_WIDTH_SET(dev_ctx
->ch_fd
[dev_idx
], width
);
596 tv
.tv_sec
= TAPI_LL_DEV_SELECT_TIMEOUT_MS
/ 1000;
597 tv
.tv_usec
= (TAPI_LL_DEV_SELECT_TIMEOUT_MS
% 1000) * 1000;
603 /* Update the local file descriptor by the copy in the task parameter */
604 memcpy((void *) &trfds
, (void*) &rfds
, sizeof(fd_set
));
606 sretval
= select(width
+ 1, &trfds
, NULL
, NULL
, &tv
);
608 if (!strm
->run_flag
) {
612 /* error or timeout on select */
617 /* Check device control channel*/
618 if (FD_ISSET(dev_ctx
->dev_fd
, &trfds
)) {
619 if (tapi_dev_event_handler(strm
) != PJ_SUCCESS
) {
620 PJ_LOG(1,(THIS_FILE
, "TAPI: event hanldler failed!"));
625 /* Check device data channel*/
626 if (FD_ISSET(dev_ctx
->ch_fd
[dev_idx
], &trfds
)) {
627 if (tapi_dev_data_handler(strm
) != PJ_SUCCESS
) {
628 PJ_LOG(1,(THIS_FILE
, "TAPI: data hanldler failed!"));
634 PJ_LOG(1,(THIS_FILE
, "TAPI: thread stopping..."));
639 /****************************************************************************
641 ****************************************************************************/
643 /* Init tapi audio driver. */
644 pjmedia_aud_dev_factory
* pjmedia_tapi_factory(pj_pool_factory
*pf
)
646 struct tapi_aud_factory
*f
;
649 TRACE_((THIS_FILE
, "pjmedia_tapi_factory()"));
651 pool
= pj_pool_create(pf
, "tapi", 512, 512, NULL
);
652 f
= PJ_POOL_ZALLOC_T(pool
, struct tapi_aud_factory
);
655 f
->base
.op
= &tapi_fact_op
;
660 /* API: init factory */
661 static pj_status_t
factory_init(pjmedia_aud_dev_factory
*f
)
663 struct tapi_aud_factory
*af
= (struct tapi_aud_factory
*)f
;
666 TRACE_((THIS_FILE
, "factory_init()"));
668 /* Enumerate sound devices */
669 af
->dev_count
= TAPI_AUDIO_DEV_NUM
;
671 af
->dev_info
= (pjmedia_aud_dev_info
*)
672 pj_pool_calloc(af
->pool
, af
->dev_count
, sizeof(pjmedia_aud_dev_info
));
674 for (c
= 0; c
< af
->dev_count
; c
++) {
675 pj_ansi_sprintf(af
->dev_info
[c
].name
,"%s_%02d", TAPI_BASE_NAME
, c
);
677 af
->dev_info
[c
].input_count
= af
->dev_info
[c
].output_count
= 1;
678 af
->dev_info
[c
].default_samples_per_sec
= TAPI_LL_DEV_ENC_SMPL_PER_SEC
;
679 pj_ansi_strcpy(af
->dev_info
[c
].driver
, "/dev/vmmc");
681 af
->dev_info
[c
].caps
= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING
|
682 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY
|
683 PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY
;
685 af
->dev_info
[c
].routes
= PJMEDIA_AUD_DEV_ROUTE_DEFAULT
;
688 /* Initialize TAPI device(s)*/
689 if (tapi_dev_start(af
) != PJ_SUCCESS
) {
690 TRACE_((THIS_FILE
, "ERROR - TAPI device init failed!"));
697 /* API: destroy factory */
698 static pj_status_t
factory_destroy(pjmedia_aud_dev_factory
*f
)
700 struct tapi_aud_factory
*af
= (struct tapi_aud_factory
*)f
;
702 pj_status_t status
= PJ_SUCCESS
;
704 TRACE_((THIS_FILE
, "factory_destroy()"));
706 /* Stop TAPI device*/
707 if (tapi_dev_stop(f
) != PJ_SUCCESS
) {
708 TRACE_((THIS_FILE
, "ERROR - TAPI device stop failed!"));
709 status
= PJ_EUNKNOWN
;
714 pj_pool_release(pool
);
719 /* API: get number of devices */
720 static unsigned factory_get_dev_count(pjmedia_aud_dev_factory
*f
)
722 struct tapi_aud_factory
*af
= (struct tapi_aud_factory
*)f
;
723 TRACE_((THIS_FILE
, "factory_get_dev_count()"));
725 return af
->dev_count
;
728 /* API: get device info */
729 static pj_status_t
factory_get_dev_info(pjmedia_aud_dev_factory
*f
,
731 pjmedia_aud_dev_info
*info
)
733 struct tapi_aud_factory
*af
= (struct tapi_aud_factory
*)f
;
735 TRACE_((THIS_FILE
, "factory_get_dev_info()"));
736 PJ_ASSERT_RETURN(index
< af
->dev_count
, PJMEDIA_EAUD_INVDEV
);
738 pj_memcpy(info
, &af
->dev_info
[index
], sizeof(*info
));
743 /* API: create default device parameter */
744 static pj_status_t
factory_default_param(pjmedia_aud_dev_factory
*f
,
746 pjmedia_aud_param
*param
)
748 struct tapi_aud_factory
*af
= (struct tapi_aud_factory
*)f
;
749 struct pjmedia_aud_dev_info
*di
= &af
->dev_info
[index
];
751 TRACE_((THIS_FILE
, "factory_default_param."));
752 PJ_ASSERT_RETURN(index
< af
->dev_count
, PJMEDIA_EAUD_INVDEV
);
754 pj_bzero(param
, sizeof(*param
));
755 if (di
->input_count
&& di
->output_count
) {
756 param
->dir
= PJMEDIA_DIR_CAPTURE_PLAYBACK
;
757 param
->rec_id
= index
;
758 param
->play_id
= index
;
759 } else if (di
->input_count
) {
760 param
->dir
= PJMEDIA_DIR_CAPTURE
;
761 param
->rec_id
= index
;
762 param
->play_id
= PJMEDIA_AUD_INVALID_DEV
;
763 } else if (di
->output_count
) {
764 param
->dir
= PJMEDIA_DIR_PLAYBACK
;
765 param
->play_id
= index
;
766 param
->rec_id
= PJMEDIA_AUD_INVALID_DEV
;
768 return PJMEDIA_EAUD_INVDEV
;
771 param
->clock_rate
= TAPI_LL_DEV_ENC_SMPL_PER_SEC
; //di->default_samples_per_sec;
772 param
->channel_count
= 1;
773 param
->samples_per_frame
= TAPI_LL_DEV_ENC_SMPL_PER_FRAME
;
774 param
->bits_per_sample
= TAPI_LL_DEV_ENC_BITS_PER_SMPLS
;
775 param
->flags
= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE
| di
->caps
;
776 param
->output_route
= PJMEDIA_AUD_DEV_ROUTE_DEFAULT
;
781 /* API: create stream */
782 static pj_status_t
factory_create_stream(pjmedia_aud_dev_factory
*f
,
783 const pjmedia_aud_param
*param
,
784 pjmedia_aud_rec_cb rec_cb
,
785 pjmedia_aud_play_cb play_cb
,
787 pjmedia_aud_stream
**p_aud_strm
)
789 struct tapi_aud_factory
*af
= (struct tapi_aud_factory
*)f
;
791 struct tapi_aud_stream
*strm
;
794 TRACE_((THIS_FILE
, "factory_create_stream()"));
796 /* Can only support 16bits per sample */
797 PJ_ASSERT_RETURN(param
->bits_per_sample
== TAPI_LL_DEV_ENC_BITS_PER_SMPLS
, PJ_EINVAL
);
798 printf("param->clock_rate = %d, samples_per_frame = %d\n", param
->clock_rate
, param
->samples_per_frame
);
799 PJ_ASSERT_RETURN(param
->clock_rate
== TAPI_LL_DEV_ENC_SMPL_PER_SEC
, PJ_EINVAL
);
801 PJ_ASSERT_RETURN(param
->samples_per_frame
== TAPI_LL_DEV_ENC_SMPL_PER_FRAME
, PJ_EINVAL
);
803 /* Can only support bidirectional stream */
804 PJ_ASSERT_RETURN(param
->dir
& PJMEDIA_DIR_CAPTURE_PLAYBACK
, PJ_EINVAL
);
806 /* Initialize our stream data */
807 pool
= pj_pool_create(af
->pf
, "tapi-dev", 1000, 1000, NULL
);
808 PJ_ASSERT_RETURN(pool
!= NULL
, PJ_ENOMEM
);
810 strm
= PJ_POOL_ZALLOC_T(pool
, struct tapi_aud_stream
);
812 strm
->rec_cb
= rec_cb
;
813 strm
->play_cb
= play_cb
;
814 strm
->user_data
= user_data
;
815 pj_memcpy(&strm
->param
, param
, sizeof(*param
));
817 if ((strm
->param
.flags
& PJMEDIA_AUD_DEV_CAP_EXT_FORMAT
) == 0) {
818 strm
->param
.ext_fmt
.id
= PJMEDIA_FORMAT_L16
;
821 strm
->timestamp
.u64
= 0;
822 strm
->dev_ctx
= &(af
->dev_ctx
);
824 /* Create and start the thread */
825 status
= pj_thread_create(pool
, "tapi", &tapi_dev_thread
, strm
, 0, 0,
827 if (status
!= PJ_SUCCESS
) {
828 stream_destroy(&strm
->base
);
833 strm
->base
.op
= &tapi_strm_op
;
834 *p_aud_strm
= &strm
->base
;
839 /****************************************************************************
842 /* API: Get stream info. */
843 static pj_status_t
stream_get_param(pjmedia_aud_stream
*s
,
844 pjmedia_aud_param
*pi
)
846 struct tapi_aud_stream
*strm
= (struct tapi_aud_stream
*)s
;
848 PJ_ASSERT_RETURN(strm
&& pi
, PJ_EINVAL
);
850 pj_memcpy(pi
, &strm
->param
, sizeof(*pi
));
851 /* Update the volume setting */
852 if (stream_get_cap(s
, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING
,
853 &pi
->output_vol
) == PJ_SUCCESS
)
855 pi
->flags
|= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING
;
858 if (stream_get_cap(s
, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY
,
859 &pi
->output_latency_ms
) == PJ_SUCCESS
)
861 pi
->flags
|= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY
;
864 if (stream_get_cap(s
, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY
,
865 &pi
->input_latency_ms
) == PJ_SUCCESS
)
867 pi
->flags
|= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY
;
873 /* API: get capability */
874 static pj_status_t
stream_get_cap(pjmedia_aud_stream
*s
,
875 pjmedia_aud_dev_cap cap
,
878 struct tapi_aud_stream
*strm
= (struct tapi_aud_stream
*)s
;
881 PJ_ASSERT_RETURN(strm
&& pval
, PJ_EINVAL
);
883 if (cap
==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING
&& strm
->play_strm
->queue
)
886 status
= AudioQueueGetParameter(strm
->play_strm
->queue
,
887 kAudioQueueParam_Volume
, &vol
);
890 *(unsigned*)pval
= (vol
* 100);
894 else if (cap
==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY
&& strm
->play_strm
->queue
)
897 UInt32 size
= sizeof(lat
);
898 status
= AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency
,
902 *(unsigned*)pval
= lat
* 1000;
906 else if (cap
==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE
&& strm
->play_strm
->queue
)
908 *(pjmedia_aud_dev_route
*)pval
= strm
->param
.output_route
;
911 else if (cap
==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY
&& strm
->rec_strm
->queue
)
914 UInt32 size
= sizeof(lat
);
915 status
= AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency
,
919 *(unsigned*)pval
= lat
* 1000;
925 PJ_LOG(1, (THIS_FILE
, "AudioQueueGetParameter/AudioSessionGetProperty err %d", status
));
926 return PJMEDIA_EAUD_INVCAP
;
932 /* API: set capability */
933 static pj_status_t
stream_set_cap(pjmedia_aud_stream
*s
,
934 pjmedia_aud_dev_cap cap
,
937 struct tapi_aud_stream
*strm
= (struct tapi_aud_stream
*)s
;
940 PJ_ASSERT_RETURN(strm
&& pval
, PJ_EINVAL
);
942 if (strm
->play_strm
->queue
)
945 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING
:
947 /* Output volume setting */
948 unsigned vol
= *(unsigned*)pval
;
954 status
= AudioQueueSetParameter(strm
->play_strm
->queue
, kAudioQueueParam_Volume
,
958 PJ_LOG(1, (THIS_FILE
, "AudioQueueSetParameter err %d", status
));
959 return PJMEDIA_EAUD_SYSERR
;
961 strm
->param
.output_vol
= *(unsigned*)pval
;
964 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE
:
966 pjmedia_aud_dev_route r
= *(const pjmedia_aud_dev_route
*)pval
;
967 UInt32 route
= (r
== PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER
?
968 kAudioSessionOverrideAudioRoute_Speaker
:
969 kAudioSessionOverrideAudioRoute_None
);
971 status
= AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute
,
972 sizeof(route
), &route
);
975 PJ_LOG(1, (THIS_FILE
, "AudioSessionSetProperty err %d", status
));
976 return PJMEDIA_EAUD_SYSERR
;
978 strm
->param
.output_route
= r
;
982 return PJMEDIA_EAUD_INVCAP
;
986 return PJMEDIA_EAUD_INVCAP
;
992 /* API: Start stream. */
993 static pj_status_t
stream_start(pjmedia_aud_stream
*s
)
995 struct tapi_aud_stream
*strm
= (struct tapi_aud_stream
*)s
;
996 tapi_ctx
*dev_ctx
= strm
->dev_ctx
;
999 TRACE_((THIS_FILE
, "stream_start()"));
1001 dev_idx
= strm
->param
.rec_id
;
1006 /* API: Stop stream. */
1007 static pj_status_t
stream_stop(pjmedia_aud_stream
*s
)
1009 struct tapi_aud_stream
*strm
= (struct tapi_aud_stream
*)s
;
1010 tapi_ctx
*dev_ctx
= strm
->dev_ctx
;
1011 pj_uint32_t dev_idx
;
1013 TRACE_((THIS_FILE
, "stream_stop()"));
1015 dev_idx
= strm
->param
.rec_id
;
1018 if (tapi_dev_codec_control(dev_ctx
->ch_fd
[dev_idx
], 0) != PJ_SUCCESS
) {
1019 TRACE_((THIS_FILE
, "ERROR - codec start failed!"));
1027 /* API: Destroy stream. */
1028 static pj_status_t
stream_destroy(pjmedia_aud_stream
*s
)
1030 pj_status_t state
= PJ_SUCCESS
;
1031 struct tapi_aud_stream
*stream
= (struct tapi_aud_stream
*)s
;
1034 PJ_ASSERT_RETURN(stream
!= NULL
, PJ_EINVAL
);
1036 TRACE_((THIS_FILE
, "stream_destroy()"));
1038 stream_stop(stream
);
1040 stream
->run_flag
= 0;
1042 /* Stop the stream thread */
1045 pj_thread_join(stream
->thread
);
1046 pj_thread_destroy(stream
->thread
);
1047 stream
->thread
= NULL
;
1050 pool
= stream
->pool
;
1051 pj_bzero(stream
, sizeof(stream
));
1052 pj_pool_release(pool
);
1057 #endif /* PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE */