blob: a5902910491c7f6ba57edfa594deeeeadd91edf3 [file] [log] [blame]
Nanang Izzuddin8cdba462009-01-29 20:06:28 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia-codec/passthrough.h>
21#include <pjmedia/codec.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/endpoint.h>
24#include <pjmedia/port.h>
25#include <pj/assert.h>
26#include <pj/log.h>
27#include <pj/pool.h>
28#include <pj/string.h>
29#include <pj/os.h>
30
31/*
32 * Only build this file if PJMEDIA_HAS_PASSTHROUGH_CODECS != 0
33 */
34#if defined(PJMEDIA_HAS_PASSTHROUGH_CODECS) && PJMEDIA_HAS_PASSTHROUGH_CODECS!=0
35
36#define THIS_FILE "passthrough.c"
37
38
39/* Prototypes for passthrough codecs factory */
40static pj_status_t test_alloc( pjmedia_codec_factory *factory,
41 const pjmedia_codec_info *id );
42static pj_status_t default_attr( pjmedia_codec_factory *factory,
43 const pjmedia_codec_info *id,
44 pjmedia_codec_param *attr );
45static pj_status_t enum_codecs( pjmedia_codec_factory *factory,
46 unsigned *count,
47 pjmedia_codec_info codecs[]);
48static pj_status_t alloc_codec( pjmedia_codec_factory *factory,
49 const pjmedia_codec_info *id,
50 pjmedia_codec **p_codec);
51static pj_status_t dealloc_codec( pjmedia_codec_factory *factory,
52 pjmedia_codec *codec );
53
54/* Prototypes for passthrough codecs implementation. */
55static pj_status_t codec_init( pjmedia_codec *codec,
56 pj_pool_t *pool );
57static pj_status_t codec_open( pjmedia_codec *codec,
58 pjmedia_codec_param *attr );
59static pj_status_t codec_close( pjmedia_codec *codec );
60static pj_status_t codec_modify(pjmedia_codec *codec,
61 const pjmedia_codec_param *attr );
62static pj_status_t codec_parse( pjmedia_codec *codec,
63 void *pkt,
64 pj_size_t pkt_size,
65 const pj_timestamp *ts,
66 unsigned *frame_cnt,
67 pjmedia_frame frames[]);
68static pj_status_t codec_encode( pjmedia_codec *codec,
69 const struct pjmedia_frame *input,
70 unsigned output_buf_len,
71 struct pjmedia_frame *output);
72static pj_status_t codec_decode( pjmedia_codec *codec,
73 const struct pjmedia_frame *input,
74 unsigned output_buf_len,
75 struct pjmedia_frame *output);
76static pj_status_t codec_recover( pjmedia_codec *codec,
77 unsigned output_buf_len,
78 struct pjmedia_frame *output);
79
80/* Definition for passthrough codecs operations. */
81static pjmedia_codec_op codec_op =
82{
83 &codec_init,
84 &codec_open,
85 &codec_close,
86 &codec_modify,
87 &codec_parse,
88 &codec_encode,
89 &codec_decode,
90 &codec_recover
91};
92
93/* Definition for passthrough codecs factory operations. */
94static pjmedia_codec_factory_op codec_factory_op =
95{
96 &test_alloc,
97 &default_attr,
98 &enum_codecs,
99 &alloc_codec,
100 &dealloc_codec
101};
102
103/* Passthrough codecs factory */
104static struct codec_factory {
105 pjmedia_codec_factory base;
106 pjmedia_endpt *endpt;
107 pj_pool_t *pool;
108 pj_mutex_t *mutex;
109} codec_factory;
110
111/* Passthrough codecs private data. */
112typedef struct codec_private {
113 pj_pool_t *pool; /**< Pool for each instance. */
114 int codec_idx; /**< Codec index. */
115 void *codec_setting; /**< Specific codec setting. */
116 pj_uint16_t avg_frame_size; /**< Average of frame size. */
117} codec_private_t;
118
119
120
121/* CUSTOM CALLBACKS */
122
123/* Parse frames from a packet. Default behaviour of frame parsing is
124 * just separating frames based on calculating frame length derived
125 * from bitrate. Implement this callback when the default behaviour is
126 * unapplicable.
127 */
128typedef pj_status_t (*parse_cb)(codec_private_t *codec_data, void *pkt,
129 pj_size_t pkt_size, const pj_timestamp *ts,
130 unsigned *frame_cnt, pjmedia_frame frames[]);
131
132/* Pack frames into a packet. Default behaviour of packing frames is
133 * just stacking the frames with octet aligned without adding any
134 * payload header. Implement this callback when the default behaviour is
135 * unapplicable.
136 */
137typedef pj_status_t (*pack_cb)(codec_private_t *codec_data,
138 const struct pjmedia_frame_ext *input,
139 unsigned output_buf_len,
140 struct pjmedia_frame *output);
141
142
143/* Custom callback implementations. */
144static pj_status_t parse_amr( codec_private_t *codec_data, void *pkt,
145 pj_size_t pkt_size, const pj_timestamp *ts,
146 unsigned *frame_cnt, pjmedia_frame frames[]);
147static pj_status_t pack_amr ( codec_private_t *codec_data,
148 const struct pjmedia_frame_ext *input,
149 unsigned output_buf_len,
150 struct pjmedia_frame *output);
151
152
153/* Passthrough codec implementation descriptions. */
154static struct codec_desc {
155 int enabled; /* Is this codec enabled? */
156 const char *name; /* Codec name. */
157 pj_uint8_t pt; /* Payload type. */
158 pjmedia_fourcc format; /* Source format. */
159 unsigned clock_rate; /* Codec's clock rate. */
160 unsigned channel_count; /* Codec's channel count. */
161 unsigned samples_per_frame; /* Codec's samples count. */
162 unsigned def_bitrate; /* Default bitrate of this codec. */
163 unsigned max_bitrate; /* Maximum bitrate of this codec. */
164 pj_uint8_t frm_per_pkt; /* Default num of frames per packet.*/
165 parse_cb parse; /* Callback to parse bitstream. */
166 pack_cb pack; /* Callback to pack bitstream. */
167 pjmedia_codec_fmtp dec_fmtp; /* Decoder's fmtp params. */
168}
169
170codec_desc[] =
171{
172# if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
173 {1, "AMR", PJMEDIA_RTP_PT_AMR, PJMEDIA_FOURCC_AMR,
174 8000, 1, 160,
175 5900, 12200, 4,
176 &parse_amr, &pack_amr
177 /*, {1, {{{"octet-align", 11}, {"1", 1}}} } */
178 },
179# endif
180
181# if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
182 {1, "G729", PJMEDIA_RTP_PT_G729, PJMEDIA_FOURCC_G729,
183 8000, 1, 80,
184 8000, 11800, 2
185 },
186# endif
187
188# if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC
189 {1, "iLBC", PJMEDIA_RTP_PT_ILBC, PJMEDIA_FOURCC_ILBC,
190 8000, 1, 80,
191 8000, 11800, 2,
192 NULL, NULL,
193 {1, {{{"mode", 4}, {"30", 2}}} }
194 },
195# endif
196
197# if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU
198 {1, "PCMU", PJMEDIA_RTP_PT_PCMU, PJMEDIA_FOURCC_G711U,
199 8000, 1, 80,
200 64000, 64000, 2
201 },
202# endif
203
204# if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA
205 {1, "PCMA", PJMEDIA_RTP_PT_PCMA, PJMEDIA_FOURCC_G711A,
206 8000, 1, 80,
207 64000, 64000, 2
208 },
209# endif
210};
211
212#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
213
214#include <pjmedia-codec/amr_helper.h>
215
216typedef struct amr_settings_t {
217 pjmedia_codec_amr_pack_setting enc_setting;
218 pjmedia_codec_amr_pack_setting dec_setting;
219 pj_int8_t enc_mode;
220} amr_settings_t;
221
222
223/* Pack AMR payload */
224static pj_status_t pack_amr ( codec_private_t *codec_data,
225 const struct pjmedia_frame_ext *input,
226 unsigned output_buf_len,
227 struct pjmedia_frame *output)
228{
229 enum {MAX_FRAMES_PER_PACKET = 16};
230
231 pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
232 unsigned nframes = 0;
233 pjmedia_codec_amr_bit_info *info;
234
235 PJ_TODO(DEFINE_AMR_FRAME_INFO_FOR_PJMEDIA_FRAME_EXT);
236
237#if 0
238
239 pj_uint8_t *r; /* Read cursor */
240 pj_uint8_t SID_FT;
241 pjmedia_codec_amr_pack_setting *setting;
242
243 setting = &((amr_settings_t*)codec_data->codec_setting)->enc_setting;
244
245 SID_FT = (pj_uint8_t)(setting->amr_nb? 8 : 9);
246
247 /* Align pkt buf right */
248 r = (pj_uint8_t*)pkt + max_pkt_size - *pkt_size;
249 pj_memmove(r, pkt, *pkt_size);
250
251 /* Get frames */
252 for (;;) {
253 pj_bool_t eof;
254 pj_uint16_t info_;
255
256 info_ = *((pj_uint16_t*)r);
257 eof = ((info_ & 0x40) != 0);
258
259 info = (pjmedia_codec_amr_bit_info*) &frames[nframes].bit_info;
260 pj_bzero(info, sizeof(*info));
261 info->frame_type = (pj_uint8_t)(info_ & 0x0F);
262 info->good_quality = 1;
263 info->mode = (pj_int8_t) ((info_ >> 8) & 0x0F);
264
265 frames[nframes].buf = r + 2;
266 frames[nframes].size = info->frame_type <= SID_FT ?
267 pjmedia_codec_amrnb_framelen[info->frame_type] :
268 0;
269
270 r += frames[nframes].size + 2;
271
272 /* Last frame */
273 if (++nframes >= MAX_FRAMES_PER_PACKET || eof)
274 break;
275 }
276
277 /* Pack */
278 *pkt_size = max_pkt_size;
279
280 return pjmedia_codec_amr_pack(frames, nframes, setting, pkt, pkt_size);
281#endif
282
283 return PJ_ENOTSUP;
284}
285
286
287/* Parse AMR payload into frames. */
288static pj_status_t parse_amr(codec_private_t *codec_data, void *pkt,
289 pj_size_t pkt_size, const pj_timestamp *ts,
290 unsigned *frame_cnt, pjmedia_frame frames[])
291{
292 amr_settings_t* s = (amr_settings_t*)codec_data->codec_setting;
293 pjmedia_codec_amr_pack_setting *setting;
294 pj_status_t status;
295 pj_uint8_t cmr;
296
297 setting = &s->dec_setting;
298
299 status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, setting, frames,
300 frame_cnt, &cmr);
301 if (status != PJ_SUCCESS)
302 return status;
303
304 /* Check Change Mode Request. */
305 if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) {
306 s->enc_mode = cmr;
307 }
308
309 return PJ_SUCCESS;
310}
311
312#endif /* PJMEDIA_HAS_PASSTROUGH_CODEC_AMR */
313
314
315/*
316 * Initialize and register passthrough codec factory to pjmedia endpoint.
317 */
318PJ_DEF(pj_status_t) pjmedia_codec_passthrough_init( pjmedia_endpt *endpt )
319{
320 pjmedia_codec_mgr *codec_mgr;
321 pj_status_t status;
322
323 if (codec_factory.pool != NULL) {
324 /* Already initialized. */
325 return PJ_SUCCESS;
326 }
327
328 /* Create passthrough codec factory. */
329 codec_factory.base.op = &codec_factory_op;
330 codec_factory.base.factory_data = NULL;
331 codec_factory.endpt = endpt;
332
333 codec_factory.pool = pjmedia_endpt_create_pool(endpt, "Passthrough codecs",
334 4000, 4000);
335 if (!codec_factory.pool)
336 return PJ_ENOMEM;
337
338 /* Create mutex. */
339 status = pj_mutex_create_simple(codec_factory.pool, "Passthrough codecs",
340 &codec_factory.mutex);
341 if (status != PJ_SUCCESS)
342 goto on_error;
343
344 /* Get the codec manager. */
345 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
346 if (!codec_mgr) {
347 status = PJ_EINVALIDOP;
348 goto on_error;
349 }
350
351 /* Register codec factory to endpoint. */
352 status = pjmedia_codec_mgr_register_factory(codec_mgr,
353 &codec_factory.base);
354 if (status != PJ_SUCCESS)
355 goto on_error;
356
357 /* Done. */
358 return PJ_SUCCESS;
359
360on_error:
361 pj_pool_release(codec_factory.pool);
362 codec_factory.pool = NULL;
363 return status;
364}
365
366/*
367 * Unregister passthrough codecs factory from pjmedia endpoint.
368 */
369PJ_DEF(pj_status_t) pjmedia_codec_passthrough_deinit(void)
370{
371 pjmedia_codec_mgr *codec_mgr;
372 pj_status_t status;
373
374 if (codec_factory.pool == NULL) {
375 /* Already deinitialized */
376 return PJ_SUCCESS;
377 }
378
379 pj_mutex_lock(codec_factory.mutex);
380
381 /* Get the codec manager. */
382 codec_mgr = pjmedia_endpt_get_codec_mgr(codec_factory.endpt);
383 if (!codec_mgr) {
384 pj_pool_release(codec_factory.pool);
385 codec_factory.pool = NULL;
386 return PJ_EINVALIDOP;
387 }
388
389 /* Unregister passthrough codecs factory. */
390 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
391 &codec_factory.base);
392
393 /* Destroy mutex. */
394 pj_mutex_destroy(codec_factory.mutex);
395
396 /* Destroy pool. */
397 pj_pool_release(codec_factory.pool);
398 codec_factory.pool = NULL;
399
400 return status;
401}
402
403/*
404 * Check if factory can allocate the specified codec.
405 */
406static pj_status_t test_alloc( pjmedia_codec_factory *factory,
407 const pjmedia_codec_info *info )
408{
409 unsigned i;
410
411 PJ_UNUSED_ARG(factory);
412
413 /* Type MUST be audio. */
414 if (info->type != PJMEDIA_TYPE_AUDIO)
415 return PJMEDIA_CODEC_EUNSUP;
416
417 for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
418 pj_str_t name = pj_str((char*)codec_desc[i].name);
419 if ((pj_stricmp(&info->encoding_name, &name) == 0) &&
420 (info->clock_rate == (unsigned)codec_desc[i].clock_rate) &&
421 (info->channel_cnt == (unsigned)codec_desc[i].channel_count) &&
422 (codec_desc[i].enabled))
423 {
424 return PJ_SUCCESS;
425 }
426 }
427
428 /* Unsupported, or mode is disabled. */
429 return PJMEDIA_CODEC_EUNSUP;
430}
431
432/*
433 * Generate default attribute.
434 */
435static pj_status_t default_attr ( pjmedia_codec_factory *factory,
436 const pjmedia_codec_info *id,
437 pjmedia_codec_param *attr )
438{
439 unsigned i;
440
441 PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL);
442
443 pj_bzero(attr, sizeof(pjmedia_codec_param));
444
445 for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
446 pj_str_t name = pj_str((char*)codec_desc[i].name);
447 if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
448 (id->clock_rate == (unsigned)codec_desc[i].clock_rate) &&
449 (id->channel_cnt == (unsigned)codec_desc[i].channel_count) &&
450 (id->pt == (unsigned)codec_desc[i].pt))
451 {
452 attr->info.pt = (pj_uint8_t)id->pt;
453 attr->info.channel_cnt = codec_desc[i].channel_count;
454 attr->info.clock_rate = codec_desc[i].clock_rate;
455 attr->info.avg_bps = codec_desc[i].def_bitrate;
456 attr->info.max_bps = codec_desc[i].max_bitrate;
457 attr->info.pcm_bits_per_sample = 16;
458 attr->info.frm_ptime = (pj_uint16_t)
459 (codec_desc[i].samples_per_frame * 1000 /
460 codec_desc[i].channel_count /
461 codec_desc[i].clock_rate);
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000462 attr->info.format = codec_desc[i].format;
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000463
464 /* Default flags. */
465 attr->setting.frm_per_pkt = codec_desc[i].frm_per_pkt;
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000466 attr->setting.plc = 0;
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000467 attr->setting.penh= 0;
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000468 attr->setting.vad = 0;
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000469 attr->setting.cng = attr->setting.vad;
470 attr->setting.dec_fmtp = codec_desc[i].dec_fmtp;
471
472 if (attr->setting.vad == 0) {
473#if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
474 if (id->pt == PJMEDIA_RTP_PT_G729) {
475 /* Signal G729 Annex B is being disabled */
476 attr->setting.dec_fmtp.cnt = 1;
477 pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb");
478 pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no");
479 }
480#endif
481 }
482
483 return PJ_SUCCESS;
484 }
485 }
486
487 return PJMEDIA_CODEC_EUNSUP;
488}
489
490/*
491 * Enum codecs supported by this factory.
492 */
493static pj_status_t enum_codecs( pjmedia_codec_factory *factory,
494 unsigned *count,
495 pjmedia_codec_info codecs[])
496{
497 unsigned max;
498 unsigned i;
499
500 PJ_UNUSED_ARG(factory);
501 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
502
503 max = *count;
504
505 for (i = 0, *count = 0; i < PJ_ARRAY_SIZE(codec_desc) && *count < max; ++i)
506 {
507 if (!codec_desc[i].enabled)
508 continue;
509
510 pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
511 codecs[*count].encoding_name = pj_str((char*)codec_desc[i].name);
512 codecs[*count].pt = codec_desc[i].pt;
513 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
514 codecs[*count].clock_rate = codec_desc[i].clock_rate;
515 codecs[*count].channel_cnt = codec_desc[i].channel_count;
516
517 ++*count;
518 }
519
520 return PJ_SUCCESS;
521}
522
523/*
524 * Allocate a new codec instance.
525 */
526static pj_status_t alloc_codec( pjmedia_codec_factory *factory,
527 const pjmedia_codec_info *id,
528 pjmedia_codec **p_codec)
529{
530 codec_private_t *codec_data;
531 pjmedia_codec *codec;
532 int idx;
533 pj_pool_t *pool;
534 unsigned i;
535
536 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
537 PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL);
538
539 pj_mutex_lock(codec_factory.mutex);
540
541 /* Find codec's index */
542 idx = -1;
543 for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
544 pj_str_t name = pj_str((char*)codec_desc[i].name);
545 if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
546 (id->clock_rate == (unsigned)codec_desc[i].clock_rate) &&
547 (id->channel_cnt == (unsigned)codec_desc[i].channel_count) &&
548 (codec_desc[i].enabled))
549 {
550 idx = i;
551 break;
552 }
553 }
554 if (idx == -1) {
555 *p_codec = NULL;
556 return PJMEDIA_CODEC_EUNSUP;
557 }
558
559 /* Create pool for codec instance */
560 pool = pjmedia_endpt_create_pool(codec_factory.endpt, "passthroughcodec",
561 512, 512);
562 codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
563 codec->op = &codec_op;
564 codec->factory = factory;
565 codec->codec_data = PJ_POOL_ZALLOC_T(pool, codec_private_t);
566 codec_data = (codec_private_t*) codec->codec_data;
567 codec_data->pool = pool;
568 codec_data->codec_idx = idx;
569
570 pj_mutex_unlock(codec_factory.mutex);
571
572 *p_codec = codec;
573 return PJ_SUCCESS;
574}
575
576/*
577 * Free codec.
578 */
579static pj_status_t dealloc_codec( pjmedia_codec_factory *factory,
580 pjmedia_codec *codec )
581{
582 codec_private_t *codec_data;
583
584 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
585 PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL);
586
587 /* Close codec, if it's not closed. */
588 codec_data = (codec_private_t*) codec->codec_data;
589 codec_close(codec);
590
591 pj_pool_release(codec_data->pool);
592
593 return PJ_SUCCESS;
594}
595
596/*
597 * Init codec.
598 */
599static pj_status_t codec_init( pjmedia_codec *codec,
600 pj_pool_t *pool )
601{
602 PJ_UNUSED_ARG(codec);
603 PJ_UNUSED_ARG(pool);
604 return PJ_SUCCESS;
605}
606
607/*
608 * Open codec.
609 */
610static pj_status_t codec_open( pjmedia_codec *codec,
611 pjmedia_codec_param *attr )
612{
613 codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
614 struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
615 pj_pool_t *pool;
616 int i, j;
617
618 pool = codec_data->pool;
619
620 /* Get bitstream size */
621 i = attr->info.avg_bps * desc->samples_per_frame;
622 j = desc->clock_rate << 3;
623 codec_data->avg_frame_size = (pj_uint16_t)(i / j);
624 if (i % j) ++codec_data->avg_frame_size;
625
626#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
627 /* Init AMR settings */
628 if (desc->pt == PJMEDIA_RTP_PT_AMR || desc->pt == PJMEDIA_RTP_PT_AMRWB) {
629 amr_settings_t *s;
630 pj_uint8_t octet_align = 0;
631 const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
632
633 /* Fetch octet-align setting. It should be fine to fetch only
634 * the decoder, since encoder & decoder must use the same setting
635 * (RFC 4867 section 8.3.1).
636 */
637 for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
638 if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name,
639 &STR_FMTP_OCTET_ALIGN) == 0)
640 {
641 octet_align=(pj_uint8_t)
642 (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
643 break;
644 }
645 }
646
647 s = PJ_POOL_ZALLOC_T(pool, amr_settings_t);
648 codec_data->codec_setting = s;
649
650 s->enc_mode = pjmedia_codec_amr_get_mode(desc->def_bitrate);
651 if (s->enc_mode < 0)
652 return PJMEDIA_CODEC_EINMODE;
653
654 s->enc_setting.amr_nb = desc->pt == PJMEDIA_RTP_PT_AMR;
655 s->enc_setting.octet_aligned = octet_align;
656 s->enc_setting.reorder = PJ_FALSE; /* Note this! passthrough codec
657 doesn't do sensitivity bits
658 reordering */
659 s->enc_setting.cmr = 15;
660
661 s->dec_setting.amr_nb = desc->pt == PJMEDIA_RTP_PT_AMR;
662 s->dec_setting.octet_aligned = octet_align;
663 s->dec_setting.reorder = PJ_FALSE; /* Note this! passthrough codec
664 doesn't do sensitivity bits
665 reordering */
666 }
667#endif
668
669 return PJ_SUCCESS;
670}
671
672/*
673 * Close codec.
674 */
675static pj_status_t codec_close( pjmedia_codec *codec )
676{
677 PJ_UNUSED_ARG(codec);
678
679 return PJ_SUCCESS;
680}
681
682
683/*
684 * Modify codec settings.
685 */
686static pj_status_t codec_modify( pjmedia_codec *codec,
687 const pjmedia_codec_param *attr )
688{
689 /* Not supported yet. */
690 PJ_UNUSED_ARG(codec);
691 PJ_UNUSED_ARG(attr);
692
693 return PJ_ENOTSUP;
694}
695
696/*
697 * Get frames in the packet.
698 */
699static pj_status_t codec_parse( pjmedia_codec *codec,
700 void *pkt,
701 pj_size_t pkt_size,
702 const pj_timestamp *ts,
703 unsigned *frame_cnt,
704 pjmedia_frame frames[])
705{
706 codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
707 struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
708 unsigned count = 0;
709
710 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
711
712 if (desc->parse != NULL) {
713 return desc->parse(codec_data, pkt, pkt_size, ts, frame_cnt, frames);
714 }
715
716 while (pkt_size >= codec_data->avg_frame_size && count < *frame_cnt) {
717 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
718 frames[count].buf = pkt;
719 frames[count].size = codec_data->avg_frame_size;
720 frames[count].timestamp.u64 = ts->u64 + count*desc->samples_per_frame;
721
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000722 pkt = (pj_uint8_t*)pkt + codec_data->avg_frame_size;
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000723 pkt_size -= codec_data->avg_frame_size;
724
725 ++count;
726 }
727
728 if (pkt_size && count < *frame_cnt) {
729 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
730 frames[count].buf = pkt;
731 frames[count].size = pkt_size;
732 frames[count].timestamp.u64 = ts->u64 + count*desc->samples_per_frame;
733 ++count;
734 }
735
736 *frame_cnt = count;
737 return PJ_SUCCESS;
738}
739
740/*
741 * Encode frames.
742 */
743static pj_status_t codec_encode( pjmedia_codec *codec,
744 const struct pjmedia_frame *input,
745 unsigned output_buf_len,
746 struct pjmedia_frame *output)
747{
748 codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
749 struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
750 const pjmedia_frame_ext *input_ = (const pjmedia_frame_ext*) input;
751
752 pj_assert(input && input->type == PJMEDIA_FRAME_TYPE_EXTENDED);
753
754 if (desc->pack != NULL) {
755 desc->pack(codec_data, input_, output_buf_len, output);
756 } else {
757 if (input_->subframe_cnt == 0) {
758 /* DTX */
759 output->buf = NULL;
760 output->size = 0;
761 output->type = PJMEDIA_FRAME_TYPE_NONE;
762 } else {
763 unsigned i;
764 pj_uint8_t *p = output->buf;
765
766 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
767 output->size = 0;
768
769 for (i = 0; i < input_->subframe_cnt; ++i) {
770 pjmedia_frame_ext_subframe *sf;
771 unsigned sf_len;
772
773 sf = pjmedia_frame_ext_get_subframe(input_, i);
774 pj_assert(sf);
775
776 sf_len = sf->bitlen >> 3;
777 if (sf->bitlen & 0x07)
778 ++sf_len;
779
780 pj_memcpy(p, sf->data, sf_len);
781 p += sf_len;
782 output->size += sf_len;
783 }
784 }
785 }
786
787 return PJ_SUCCESS;
788}
789
790/*
791 * Decode frame.
792 */
793static pj_status_t codec_decode( pjmedia_codec *codec,
794 const struct pjmedia_frame *input,
795 unsigned output_buf_len,
796 struct pjmedia_frame *output)
797{
798 codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
799 struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
800 pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
801
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000802 pj_assert(input && input->size > 0);
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000803
804#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
805 /* Need to rearrange the AMR bitstream, since the bitstream may not be
806 * started from bit 0 or may need to be reordered from sensitivity order
807 * into encoder bits order.
808 */
809 if (desc->pt == PJMEDIA_RTP_PT_AMR) {
810 pjmedia_frame frame;
811 pjmedia_codec_amr_pack_setting *setting;
812
813 setting = &((amr_settings_t*)codec_data->codec_setting)->dec_setting;
814
815 frame = *input;
816 pjmedia_codec_amr_predecode(input, setting, &frame);
817 }
818#endif
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000819 /*
820 PJ_ASSERT_RETURN(output_buf_len >= sizeof(pjmedia_frame_ext) +
821 sizeof(pjmedia_frame_ext_subframe) +
822 input->size,
823 PJMEDIA_CODEC_EFRMTOOSHORT);
824 */
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000825
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000826 pjmedia_frame_ext_append_subframe(output_, input->buf,
827 (pj_uint16_t)(input->size << 3),
828 (pj_uint16_t)desc->samples_per_frame);
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000829
830 return PJ_SUCCESS;
831}
832
833/*
834 * Recover lost frame.
835 */
836static pj_status_t codec_recover( pjmedia_codec *codec,
837 unsigned output_buf_len,
838 struct pjmedia_frame *output)
839{
Nanang Izzuddin81db8c72009-02-05 10:59:14 +0000840 codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
841 struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
842 pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
843
844 PJ_UNUSED_ARG(output_buf_len);
845
846 pjmedia_frame_ext_append_subframe(output_, NULL, 0,
847 (pj_uint16_t)desc->samples_per_frame);
848
849 return PJ_SUCCESS;
Nanang Izzuddin8cdba462009-01-29 20:06:28 +0000850}
851
852#endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
853