/* * Create sound player port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port) { pjmedia_snd_port_param param; pj_status_t status; pjmedia_snd_port_param_default(¶m); /* Normalize dev_id */ if (dev_id < 0) dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; param.base.dir = PJMEDIA_DIR_PLAYBACK; param.base.play_id = dev_id; param.base.clock_rate = clock_rate; param.base.channel_count = channel_count; param.base.samples_per_frame = samples_per_frame; param.base.bits_per_sample = bits_per_sample; param.options = options; param.ec_options = 0; return pjmedia_snd_port_create2(pool, ¶m, p_port); }
/* * Create sound recorder AEC. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port) { pjmedia_snd_port_param param; pj_status_t status; pjmedia_snd_port_param_default(¶m); status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; param.base.dir = PJMEDIA_DIR_CAPTURE; param.base.rec_id = dev_id; param.base.clock_rate = clock_rate; param.base.channel_count = channel_count; param.base.samples_per_frame = samples_per_frame; param.base.bits_per_sample = bits_per_sample; param.options = options; param.ec_options = 0; return pjmedia_snd_port_create2(pool, ¶m, p_port); }
static void record(unsigned rec_index, const char *filename) { pj_pool_t *pool = NULL; pjmedia_port *wav = NULL; pjmedia_aud_param param; pjmedia_aud_stream *strm = NULL; char line[10], *dummy; pj_status_t status; if (filename == NULL) filename = WAV_FILE; pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", 1000, 1000, NULL); status = pjmedia_wav_writer_port_create(pool, filename, 16000, 1, 320, 16, 0, 0, &wav); if (status != PJ_SUCCESS) { app_perror("Error creating WAV file", status); goto on_return; } status = pjmedia_aud_dev_default_param(rec_index, ¶m); if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_dev_default_param()", status); goto on_return; } param.dir = PJMEDIA_DIR_CAPTURE; param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info); param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info); param.channel_count = PJMEDIA_PIA_CCNT(&wav->info); param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info); status = pjmedia_aud_stream_create(¶m, &wav_rec_cb, NULL, wav, &strm); if (status != PJ_SUCCESS) { app_perror("Error opening the sound device", status); goto on_return; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("Error starting the sound device", status); goto on_return; } PJ_LOG(3,(THIS_FILE, "Recording started, press ENTER to stop")); dummy = fgets(line, sizeof(line), stdin); on_return: if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (wav) pjmedia_port_destroy(wav); if (pool) pj_pool_release(pool); }
/* * Create sound player port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port) { pjmedia_snd_port_param param; pj_status_t status; PJ_UNUSED_ARG(options); status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; param.base.dir = PJMEDIA_DIR_PLAYBACK; param.base.play_id = dev_id; param.base.clock_rate = clock_rate; param.base.channel_count = channel_count; param.base.samples_per_frame = samples_per_frame; param.base.bits_per_sample = bits_per_sample; param.options = options; return pjmedia_snd_port_create2(pool, ¶m, p_port); }
static void play_file(unsigned play_index, const char *filename) { pj_pool_t *pool = NULL; pjmedia_port *wav = NULL; pjmedia_aud_param param; pjmedia_aud_stream *strm = NULL; char line[10], *dummy; pj_status_t status; if (filename == NULL) filename = WAV_FILE; pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", 1000, 1000, NULL); status = pjmedia_wav_player_port_create(pool, filename, 20, 0, 0, &wav); if (status != PJ_SUCCESS) { app_perror("Error opening WAV file", status); goto on_return; } status = pjmedia_aud_dev_default_param(play_index, ¶m); if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_dev_default_param()", status); goto on_return; } param.dir = PJMEDIA_DIR_PLAYBACK; param.clock_rate = wav->info.clock_rate; param.samples_per_frame = wav->info.samples_per_frame; param.channel_count = wav->info.channel_count; param.bits_per_sample = wav->info.bits_per_sample; status = pjmedia_aud_stream_create(¶m, NULL, &wav_play_cb, wav, &strm); if (status != PJ_SUCCESS) { app_perror("Error opening the sound device", status); goto on_return; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("Error starting the sound device", status); goto on_return; } PJ_LOG(3,(THIS_FILE, "Playback started, press ENTER to stop")); dummy = fgets(line, sizeof(line), stdin); on_return: if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (wav) pjmedia_port_destroy(wav); if (pool) pj_pool_release(pool); }
/* Start sound */ static pj_status_t snd_start(unsigned flag) { pj_status_t status; if (strm != NULL) { app_perror("snd already open", PJ_EINVALIDOP); return PJ_EINVALIDOP; } pjmedia_aud_dev_default_param(0, ¶m); param.channel_count = CHANNEL_COUNT; param.clock_rate = CLOCK_RATE; param.samples_per_frame = SAMPLES_PER_FRAME; param.dir = (pjmedia_dir) flag; param.ext_fmt.id = PJMEDIA_FORMAT_AMR; param.ext_fmt.bitrate = 12200; param.output_route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER; status = pjmedia_aud_stream_create(¶m, &rec_cb, &play_cb, NULL, &strm); if (status != PJ_SUCCESS) { app_perror("snd open", status); return status; } rec_cnt = play_cnt = 0; pj_gettimeofday(&t_start); pjmedia_delay_buf_reset(delaybuf); status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("snd start", status); pjmedia_aud_stream_destroy(strm); strm = NULL; return status; } return PJ_SUCCESS; }
static pj_status_t open_stream( pjmedia_dir dir, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { pj_pool_t *pool; pjmedia_snd_stream *snd_strm; pjmedia_aud_param param; pj_status_t status; /* Initialize parameters */ if (dir & PJMEDIA_DIR_CAPTURE) { status = pjmedia_aud_dev_default_param(rec_id, ¶m); } else { status = pjmedia_aud_dev_default_param(play_id, ¶m); } if (status != PJ_SUCCESS) return status; param.dir = dir; param.rec_id = rec_id; param.play_id = play_id; param.clock_rate = clock_rate; param.channel_count = channel_count; param.samples_per_frame = samples_per_frame; param.bits_per_sample = bits_per_sample; /* Latencies setting */ if ((dir & PJMEDIA_DIR_CAPTURE) && g_sys.user_rec_latency) { param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; param.input_latency_ms = g_sys.user_rec_latency; } if ((dir & PJMEDIA_DIR_PLAYBACK) && g_sys.user_play_latency) { param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; param.output_latency_ms = g_sys.user_play_latency; } /* Create sound wrapper */ pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "legacy-snd", 512, 512, NULL); snd_strm = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream); snd_strm->pool = pool; snd_strm->user_rec_cb = rec_cb; snd_strm->user_play_cb = play_cb; snd_strm->user_user_data = user_data; /* Create the stream */ status = pjmedia_aud_stream_create(¶m, &snd_rec_cb, &snd_play_cb, snd_strm, &snd_strm->aud_strm); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } *p_snd_strm = snd_strm; return PJ_SUCCESS; }
static void pcap2wav(const pj_str_t *codec, const pj_str_t *wav_filename, pjmedia_aud_dev_index dev_id, const pj_str_t *srtp_crypto, const pj_str_t *srtp_key) { const pj_str_t WAV = {".wav", 4}; struct pkt { pj_uint8_t buffer[320]; pjmedia_rtp_hdr *rtp; pj_uint8_t *payload; unsigned payload_len; } pkt0; pjmedia_codec_mgr *cmgr; const pjmedia_codec_info *ci; pjmedia_codec_param param; unsigned samples_per_frame; pj_status_t status; /* Initialize all codecs */ T( pjmedia_codec_register_audio_codecs(app.mept, NULL) ); /* Create SRTP transport is needed */ #if PJMEDIA_HAS_SRTP if (srtp_crypto->slen) { pjmedia_srtp_crypto crypto; pj_bzero(&crypto, sizeof(crypto)); crypto.key = *srtp_key; crypto.name = *srtp_crypto; T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) ); T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) ); } #else PJ_UNUSED_ARG(srtp_crypto); PJ_UNUSED_ARG(srtp_key); #endif /* Read first packet */ read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, &pkt0.payload, &pkt0.payload_len, PJ_FALSE); cmgr = pjmedia_endpt_get_codec_mgr(app.mept); /* Get codec info and param for the specified payload type */ app.pt = pkt0.rtp->pt; if (app.pt >=0 && app.pt < 96) { T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) ); } else { unsigned cnt = 2; const pjmedia_codec_info *info[2]; T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt, info, NULL) ); if (cnt != 1) err_exit("Codec ID must be specified and unique!", 0); ci = info[0]; } T( pjmedia_codec_mgr_get_default_param(cmgr, ci, ¶m) ); /* Alloc and init codec */ T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) ); T( pjmedia_codec_init(app.codec, app.pool) ); T( pjmedia_codec_open(app.codec, ¶m) ); /* Init audio device or WAV file */ samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000; if (pj_strcmp2(wav_filename, "-") == 0) { pjmedia_aud_param aud_param; /* Open audio device */ T( pjmedia_aud_dev_default_param(dev_id, &aud_param) ); aud_param.dir = PJMEDIA_DIR_PLAYBACK; aud_param.channel_count = ci->channel_cnt; aud_param.clock_rate = ci->clock_rate; aud_param.samples_per_frame = samples_per_frame; T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, NULL, &app.aud_strm) ); T( pjmedia_aud_stream_start(app.aud_strm) ); } else if (pj_stristr(wav_filename, &WAV)) { /* Open WAV file */ T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr, ci->clock_rate, ci->channel_cnt, samples_per_frame, param.info.pcm_bits_per_sample, 0, 0, &app.wav) ); } else { err_exit("invalid output file", PJ_EINVAL); } /* Loop reading PCAP and writing WAV file */ for (;;) { struct pkt pkt1; pj_timestamp ts; pjmedia_frame frames[16], pcm_frame; short pcm[320]; unsigned i, frame_cnt; long samples_cnt, ts_gap; pj_assert(sizeof(pcm) >= samples_per_frame); /* Parse first packet */ ts.u64 = 0; frame_cnt = PJ_ARRAY_SIZE(frames); T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, &ts, &frame_cnt, frames) ); /* Decode and write to WAV file */ samples_cnt = 0; for (i=0; i<frame_cnt; ++i) { pjmedia_frame pcm_frame; pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; T( pjmedia_codec_decode(app.codec, &frames[i], (unsigned)pcm_frame.size, &pcm_frame) ); if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); } if (app.aud_strm) { T( wait_play(&pcm_frame) ); } samples_cnt += samples_per_frame; } /* Read next packet */ read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp, &pkt1.payload, &pkt1.payload_len, PJ_TRUE); /* Fill in the gap (if any) between pkt0 and pkt1 */ ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) - samples_cnt; while (ts_gap >= (long)samples_per_frame) { pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; if (app.codec->op->recover) { T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, &pcm_frame) ); } else { pj_bzero(pcm_frame.buf, pcm_frame.size); } if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); } if (app.aud_strm) { T( wait_play(&pcm_frame) ); } ts_gap -= samples_per_frame; } /* Next */ pkt0 = pkt1; pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer; pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer); } }
/**************************************************************************** * test: audio system test */ static void systest_audio_test(void) { enum { GOOD_MAX_INTERVAL = 5, }; const pjmedia_dir dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; pjmedia_aud_param param; pjmedia_aud_test_results result; pj_size_t textbufpos; enum gui_key key; unsigned problem_count = 0; const char *problems[16]; char drifttext[120]; test_item_t *ti; const char *title = "Audio Device Test"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This will run an automated test for about " "ten seconds or so, and display some " "statistics about your sound device. " "Please don't do anything until the test completes. " "Press OK to start, or CANCEL to skip this test.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); /* Disable sound device in pjsua first */ pjsua_set_no_snd_dev(); /* Setup parameters */ status = pjmedia_aud_dev_default_param(systest.play_id, ¶m); if (status != PJ_SUCCESS) { systest_perror("Sorry we had error in pjmedia_aud_dev_default_param()", status); pjsua_set_snd_dev(systest.rec_id, systest.play_id); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; return; } param.dir = dir; param.rec_id = systest.rec_id; param.play_id = systest.play_id; param.clock_rate = systest.media_cfg.snd_clock_rate; param.channel_count = systest.media_cfg.channel_count; param.samples_per_frame = param.clock_rate * param.channel_count * systest.media_cfg.audio_frame_ptime / 1000; /* Latency settings */ param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY); param.input_latency_ms = systest.media_cfg.snd_rec_latency; param.output_latency_ms = systest.media_cfg.snd_play_latency; /* Run the test */ status = pjmedia_aud_test(¶m, &result); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered error with the test", status); pjsua_set_snd_dev(systest.rec_id, systest.play_id); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; return; } /* Restore pjsua sound device */ pjsua_set_snd_dev(systest.rec_id, systest.play_id); /* Analyze the result! */ strcpy(textbuf, "Here are the audio statistics:\r\n"); textbufpos = strlen(textbuf); if (result.rec.frame_cnt==0) { problems[problem_count++] = "No audio frames were captured from the microphone. " "This means the audio device is not working properly."; } else { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "Rec : interval (min/max/avg/dev)=\r\n" " %u/%u/%u/%u (ms)\r\n" " max burst=%u\r\n", result.rec.min_interval, result.rec.max_interval, result.rec.avg_interval, result.rec.dev_interval, result.rec.max_burst); textbufpos = strlen(textbuf); if (result.rec.max_burst > GOOD_MAX_INTERVAL) { problems[problem_count++] = "Recording max burst is quite high"; } } if (result.play.frame_cnt==0) { problems[problem_count++] = "No audio frames were played to the speaker. " "This means the audio device is not working properly."; } else { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "Play: interval (min/max/avg/dev)=\r\n" " %u/%u/%u/%u (ms)\r\n" " burst=%u\r\n", result.play.min_interval, result.play.max_interval, result.play.avg_interval, result.play.dev_interval, result.play.max_burst); textbufpos = strlen(textbuf); if (result.play.max_burst > GOOD_MAX_INTERVAL) { problems[problem_count++] = "Playback max burst is quite high"; } } if (result.rec_drift_per_sec) { const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower"; unsigned drift = result.rec_drift_per_sec>=0 ? result.rec_drift_per_sec : -result.rec_drift_per_sec; pj_ansi_snprintf(drifttext, sizeof(drifttext), "Clock drifts detected. Capture " "is %d samples/sec %s " "than the playback device", drift, which); problems[problem_count++] = drifttext; } if (problem_count == 0) { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "\r\nThe sound device seems to be okay!"); textbufpos = strlen(textbuf); key = gui_msgbox("Audio Device Test", textbuf, WITH_OK); } else { unsigned i; pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "There could be %d problem(s) with the " "sound device:\r\n", problem_count); textbufpos = strlen(textbuf); for (i=0; i<problem_count; ++i) { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, " %d: %s\r\n", i+1, problems[i]); textbufpos = strlen(textbuf); } key = gui_msgbox(title, textbuf, WITH_OK); } ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; }
static void test_device(pjmedia_dir dir, unsigned rec_id, unsigned play_id, unsigned clock_rate, unsigned ptime, unsigned chnum) { pjmedia_aud_param param; pjmedia_aud_test_results result; pj_status_t status; if (dir & PJMEDIA_DIR_CAPTURE) { status = pjmedia_aud_dev_default_param(rec_id, ¶m); } else { status = pjmedia_aud_dev_default_param(play_id, ¶m); } if (status != PJ_SUCCESS) { app_perror("pjmedia_aud_dev_default_param()", status); return; } param.dir = dir; param.rec_id = rec_id; param.play_id = play_id; param.clock_rate = clock_rate; param.channel_count = chnum; param.samples_per_frame = clock_rate * chnum * ptime / 1000; /* Latency settings */ param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY); param.input_latency_ms = capture_lat; param.output_latency_ms = playback_lat; PJ_LOG(3,(THIS_FILE, "Performing test..")); status = pjmedia_aud_test(¶m, &result); if (status != PJ_SUCCESS) { app_perror("Test has completed with error", status); return; } PJ_LOG(3,(THIS_FILE, "Done. Result:")); if (dir & PJMEDIA_DIR_CAPTURE) { if (result.rec.frame_cnt==0) { PJ_LOG(1,(THIS_FILE, "Error: no frames captured!")); } else { PJ_LOG(3,(THIS_FILE, " %-20s: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u", "Recording result", result.rec.min_interval, result.rec.max_interval, result.rec.avg_interval, result.rec.dev_interval, result.rec.max_burst)); } } if (dir & PJMEDIA_DIR_PLAYBACK) { if (result.play.frame_cnt==0) { PJ_LOG(1,(THIS_FILE, "Error: no playback!")); } else { PJ_LOG(3,(THIS_FILE, " %-20s: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u", "Playback result", result.play.min_interval, result.play.max_interval, result.play.avg_interval, result.play.dev_interval, result.play.max_burst)); } } if (dir==PJMEDIA_DIR_CAPTURE_PLAYBACK) { if (result.rec_drift_per_sec == 0) { PJ_LOG(3,(THIS_FILE, " No clock drift detected")); } else { const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower"; unsigned drift = result.rec_drift_per_sec>=0 ? result.rec_drift_per_sec : -result.rec_drift_per_sec; PJ_LOG(3,(THIS_FILE, " Clock drifts detected. Capture device " "is running %d samples per second %s " "than the playback device", drift, which)); } } }