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); }
static int aviplay(pj_pool_t *pool, const char *fname) { pjmedia_vid_port *renderer=NULL; pjmedia_vid_port_param param; const pjmedia_video_format_info *vfi; pjmedia_video_format_detail *vfd; pjmedia_snd_port *snd_port = NULL; pj_status_t status; int rc = 0; pjmedia_avi_streams *avi_streams; pjmedia_avi_stream *vid_stream, *aud_stream; pjmedia_port *vid_port = NULL, *aud_port = NULL; pjmedia_vid_codec *codec=NULL; avi_port_t avi_port; pj_bzero(&avi_port, sizeof(avi_port)); status = pjmedia_avi_player_create_streams(pool, fname, 0, &avi_streams); if (status != PJ_SUCCESS) { PJ_PERROR(2,("", status, " Error playing %s", fname)); rc = 210; goto on_return; } vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams, 0, PJMEDIA_TYPE_VIDEO); vid_port = pjmedia_avi_stream_get_port(vid_stream); if (vid_port) { pjmedia_vid_port_param_default(¶m); status = pjmedia_vid_dev_default_param(pool, PJMEDIA_VID_DEFAULT_RENDER_DEV, ¶m.vidparam); if (status != PJ_SUCCESS) { rc = 220; goto on_return; } /* Create renderer, set it to active */ param.active = PJ_TRUE; param.vidparam.dir = PJMEDIA_DIR_RENDER; vfd = pjmedia_format_get_video_format_detail(&vid_port->info.fmt, PJ_TRUE); pjmedia_format_init_video(¶m.vidparam.fmt, vid_port->info.fmt.id, vfd->size.w, vfd->size.h, vfd->fps.num, vfd->fps.denum); vfi = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), vid_port->info.fmt.id); /* Check whether the frame is encoded */ if (!vfi || vfi->bpp == 0) { /* Yes, prepare codec */ pj_str_t codec_id_st; unsigned info_cnt = 1, i, k; const pjmedia_vid_codec_info *codec_info; pj_str_t port_name = {"codec", 5}; pj_uint8_t *enc_buf = NULL; pj_size_t enc_buf_size = 0; pjmedia_vid_dev_info rdr_info; pjmedia_port codec_port; codec_port_data_t codec_port_data; pjmedia_vid_codec_param codec_param; struct codec_fmt *codecp = NULL; /* Lookup codec */ for (i = 0; i < sizeof(codec_fmts)/sizeof(codec_fmts[0]); i++) { if (vid_port->info.fmt.id == codec_fmts[i].pjmedia_id) { codecp = &codec_fmts[i]; break; } } if (!codecp) { rc = 242; goto on_return; } pj_cstr(&codec_id_st, codecp->codec_id); status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st, &info_cnt, &codec_info, NULL); if (status != PJ_SUCCESS) { rc = 245; goto on_return; } status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, &codec_param); if (status != PJ_SUCCESS) { rc = 246; goto on_return; } pjmedia_format_copy(&codec_param.enc_fmt, ¶m.vidparam.fmt); pjmedia_vid_dev_get_info(param.vidparam.rend_id, &rdr_info); for (i=0; i<codec_info->dec_fmt_id_cnt; ++i) { for (k=0; k<rdr_info.fmt_cnt; ++k) { if (codec_info->dec_fmt_id[i]==(int)rdr_info.fmt[k].id) { param.vidparam.fmt.id = codec_info->dec_fmt_id[i]; i = codec_info->dec_fmt_id_cnt; break; } } } /* Open codec */ status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &codec); if (status != PJ_SUCCESS) { rc = 250; goto on_return; } status = pjmedia_vid_codec_init(codec, pool); if (status != PJ_SUCCESS) { rc = 251; goto on_return; } pjmedia_format_copy(&codec_param.dec_fmt, ¶m.vidparam.fmt); codec_param.dir = PJMEDIA_DIR_DECODING; codec_param.packing = PJMEDIA_VID_PACKING_WHOLE; status = pjmedia_vid_codec_open(codec, &codec_param); if (status != PJ_SUCCESS) { rc = 252; goto on_return; } /* Alloc encoding buffer */ enc_buf_size = codec_param.dec_fmt.det.vid.size.w * codec_param.dec_fmt.det.vid.size.h * 4 + 16; /*< padding, just in case */ enc_buf = pj_pool_alloc(pool,enc_buf_size); /* Init codec port */ pj_bzero(&codec_port, sizeof(codec_port)); status = pjmedia_port_info_init2(&codec_port.info, &port_name, 0x1234, PJMEDIA_DIR_ENCODING, &codec_param.dec_fmt); if (status != PJ_SUCCESS) { rc = 260; goto on_return; } pj_bzero(&codec_port_data, sizeof(codec_port_data)); codec_port_data.codec = codec; codec_port_data.src_port = vid_port; codec_port_data.enc_buf = enc_buf; codec_port_data.enc_buf_size = enc_buf_size; codec_port.get_frame = &codec_get_frame; codec_port.port_data.pdata = &codec_port_data; /* Check whether we need to convert the decoded frame */ if (codecp->need_conversion) { pjmedia_conversion_param conv_param; pjmedia_format_copy(&conv_param.src, ¶m.vidparam.fmt); pjmedia_format_copy(&conv_param.dst, ¶m.vidparam.fmt); conv_param.dst.id = codecp->dst_fmt; param.vidparam.fmt.id = conv_param.dst.id; status = pjmedia_converter_create(NULL, pool, &conv_param, &codec_port_data.conv); if (status != PJ_SUCCESS) { rc = 270; goto on_return; } } status = pjmedia_vid_port_create(pool, ¶m, &renderer); if (status != PJ_SUCCESS) { rc = 230; goto on_return; } status = pjmedia_vid_port_connect(renderer, &codec_port, PJ_FALSE); } else { status = pjmedia_vid_port_create(pool, ¶m, &renderer); if (status != PJ_SUCCESS) { rc = 230; goto on_return; } /* Connect avi port to renderer */ status = pjmedia_vid_port_connect(renderer, vid_port, PJ_FALSE); } if (status != PJ_SUCCESS) { rc = 240; goto on_return; } } aud_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams, 0, PJMEDIA_TYPE_AUDIO); aud_port = pjmedia_avi_stream_get_port(aud_stream); if (aud_port) { /* Create sound player port. */ status = pjmedia_snd_port_create_player( pool, /* pool */ -1, /* use default dev. */ PJMEDIA_PIA_SRATE(&aud_port->info),/* clock rate. */ PJMEDIA_PIA_CCNT(&aud_port->info), /* # of channels. */ PJMEDIA_PIA_SPF(&aud_port->info), /* samples per frame. */ PJMEDIA_PIA_BITS(&aud_port->info), /* bits per sample. */ 0, /* options */ &snd_port /* returned port */ ); if (status != PJ_SUCCESS) { rc = 310; goto on_return; } /* Connect file port to the sound player. * Stream playing will commence immediately. */ status = pjmedia_snd_port_connect(snd_port, aud_port); if (status != PJ_SUCCESS) { rc = 330; goto on_return; } } if (vid_port) { pjmedia_vid_dev_cb cb; pj_bzero(&cb, sizeof(cb)); avi_port.snd_port = snd_port; avi_port.vid_port = renderer; avi_port.is_running = PJ_TRUE; pjmedia_vid_port_set_cb(renderer, &cb, &avi_port); /* subscribe events */ pjmedia_event_subscribe(NULL, &avi_event_cb, &avi_port, renderer); if (snd_port) { /* Synchronize video rendering and audio playback */ pjmedia_vid_port_set_clock_src( renderer, pjmedia_snd_port_get_clock_src( snd_port, PJMEDIA_DIR_PLAYBACK)); } /* Start video streaming.. */ status = pjmedia_vid_port_start(renderer); if (status != PJ_SUCCESS) { rc = 270; goto on_return; } } while (!avi_port.is_quitting) { pj_thread_sleep(100); } on_return: if (snd_port) { pjmedia_snd_port_disconnect(snd_port); /* Without this sleep, Windows/DirectSound will repeteadly * play the last frame during destroy. */ pj_thread_sleep(100); pjmedia_snd_port_destroy(snd_port); } if (renderer) { pjmedia_event_unsubscribe(NULL, &avi_event_cb, &avi_port, renderer); pjmedia_vid_port_destroy(renderer); } if (aud_port) pjmedia_port_destroy(aud_port); if (vid_port) pjmedia_port_destroy(vid_port); if (codec) { pjmedia_vid_codec_close(codec); pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec); } return rc; }
/* * Callback when SDP negotiation has completed. * We are interested with this callback because we want to start media * as soon as SDP negotiation is completed. */ static void call_on_media_update( pjsip_inv_session *inv, pj_status_t status) { pjmedia_stream_info stream_info; const pjmedia_sdp_session *local_sdp; const pjmedia_sdp_session *remote_sdp; pjmedia_port *media_port; if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "SDP negotiation has failed", status); /* Here we should disconnect call if we're not in the middle * of initializing an UAS dialog and if this is not a re-INVITE. */ return; } /* Get local and remote SDP. * We need both SDPs to create a media session. */ status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp); status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); /* Create stream info based on the media audio SDP. */ status = pjmedia_stream_info_from_sdp(&stream_info, inv->dlg->pool, g_med_endpt, local_sdp, remote_sdp, 0); if (status != PJ_SUCCESS) { app_perror(THIS_FILE,"Unable to create audio stream info",status); return; } /* If required, we can also change some settings in the stream info, * (such as jitter buffer settings, codec settings, etc) before we * create the stream. */ /* Create new audio media stream, passing the stream info, and also the * media socket that we created earlier. */ status = pjmedia_stream_create(g_med_endpt, inv->dlg->pool, &stream_info, g_med_transport[0], NULL, &g_med_stream); if (status != PJ_SUCCESS) { app_perror( THIS_FILE, "Unable to create audio stream", status); return; } /* Start the audio stream */ status = pjmedia_stream_start(g_med_stream); if (status != PJ_SUCCESS) { app_perror( THIS_FILE, "Unable to start audio stream", status); return; } /* Get the media port interface of the audio stream. * Media port interface is basicly a struct containing get_frame() and * put_frame() function. With this media port interface, we can attach * the port interface to conference bridge, or directly to a sound * player/recorder device. */ pjmedia_stream_get_port(g_med_stream, &media_port); /* Create sound port */ pjmedia_snd_port_create(inv->pool, PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV, PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate */ PJMEDIA_PIA_CCNT(&media_port->info),/* channel count */ PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/ PJMEDIA_PIA_BITS(&media_port->info),/* bits per sample */ 0, &g_snd_port); if (status != PJ_SUCCESS) { app_perror( THIS_FILE, "Unable to create sound port", status); PJ_LOG(3,(THIS_FILE, "%d %d %d %d", PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate */ PJMEDIA_PIA_CCNT(&media_port->info),/* channel count */ PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/ PJMEDIA_PIA_BITS(&media_port->info) /* bits per sample */ )); return; } status = pjmedia_snd_port_connect(g_snd_port, media_port); /* Get the media port interface of the second stream in the session, * which is video stream. With this media port interface, we can attach * the port directly to a renderer/capture video device. */ #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) if (local_sdp->media_count > 1) { pjmedia_vid_stream_info vstream_info; pjmedia_vid_port_param vport_param; pjmedia_vid_port_param_default(&vport_param); /* Create stream info based on the media video SDP. */ status = pjmedia_vid_stream_info_from_sdp(&vstream_info, inv->dlg->pool, g_med_endpt, local_sdp, remote_sdp, 1); if (status != PJ_SUCCESS) { app_perror(THIS_FILE,"Unable to create video stream info",status); return; } /* If required, we can also change some settings in the stream info, * (such as jitter buffer settings, codec settings, etc) before we * create the video stream. */ /* Create new video media stream, passing the stream info, and also the * media socket that we created earlier. */ status = pjmedia_vid_stream_create(g_med_endpt, NULL, &vstream_info, g_med_transport[1], NULL, &g_med_vstream); if (status != PJ_SUCCESS) { app_perror( THIS_FILE, "Unable to create video stream", status); return; } /* Start the video stream */ status = pjmedia_vid_stream_start(g_med_vstream); if (status != PJ_SUCCESS) { app_perror( THIS_FILE, "Unable to start video stream", status); return; } if (vstream_info.dir & PJMEDIA_DIR_DECODING) { status = pjmedia_vid_dev_default_param( inv->pool, PJMEDIA_VID_DEFAULT_RENDER_DEV, &vport_param.vidparam); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to get default param of video " "renderer device", status); return; } /* Get video stream port for decoding direction */ pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_DECODING, &media_port); /* Set format */ pjmedia_format_copy(&vport_param.vidparam.fmt, &media_port->info.fmt); vport_param.vidparam.dir = PJMEDIA_DIR_RENDER; vport_param.active = PJ_TRUE; /* Create renderer */ status = pjmedia_vid_port_create(inv->pool, &vport_param, &g_vid_renderer); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create video renderer device", status); return; } /* Connect renderer to media_port */ status = pjmedia_vid_port_connect(g_vid_renderer, media_port, PJ_FALSE); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to connect renderer to stream", status); return; } } /* Create capturer */ if (vstream_info.dir & PJMEDIA_DIR_ENCODING) { status = pjmedia_vid_dev_default_param( inv->pool, PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vport_param.vidparam); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to get default param of video " "capture device", status); return; } /* Get video stream port for decoding direction */ pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_ENCODING, &media_port); /* Get capturer format from stream info */ pjmedia_format_copy(&vport_param.vidparam.fmt, &media_port->info.fmt); vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE; vport_param.active = PJ_TRUE; /* Create capturer */ status = pjmedia_vid_port_create(inv->pool, &vport_param, &g_vid_capturer); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create video capture device", status); return; } /* Connect capturer to media_port */ status = pjmedia_vid_port_connect(g_vid_capturer, media_port, PJ_FALSE); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to connect capturer to stream", status); return; } } /* Start streaming */ if (g_vid_renderer) { status = pjmedia_vid_port_start(g_vid_renderer); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to start video renderer", status); return; } } if (g_vid_capturer) { status = pjmedia_vid_port_start(g_vid_capturer); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to start video capturer", status); return; } } } #endif /* PJMEDIA_HAS_VIDEO */ /* Done with media. */ }
PJ_DEF(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool, pjmedia_port *dn_port, unsigned channel_count, unsigned options, pjmedia_port **p_port ) { const pj_str_t name = pj_str("stereo"); struct stereo_port *sport; unsigned samples_per_frame; /* Validate arguments. */ PJ_ASSERT_RETURN(pool && dn_port && channel_count && p_port, PJ_EINVAL); /* Only supports 16bit samples per frame */ PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16, PJMEDIA_ENCBITS); /* Validate channel counts */ PJ_ASSERT_RETURN(((PJMEDIA_PIA_CCNT(&dn_port->info)>1 && channel_count==1) || (PJMEDIA_PIA_CCNT(&dn_port->info)==1 && channel_count>1)), PJ_EINVAL); /* Create and initialize port. */ sport = PJ_POOL_ZALLOC_T(pool, struct stereo_port); PJ_ASSERT_RETURN(sport != NULL, PJ_ENOMEM); samples_per_frame = PJMEDIA_PIA_SPF(&dn_port->info) * channel_count / PJMEDIA_PIA_CCNT(&dn_port->info); pjmedia_port_info_init(&sport->base.info, &name, SIGNATURE, PJMEDIA_PIA_SRATE(&dn_port->info), channel_count, PJMEDIA_PIA_BITS(&dn_port->info), samples_per_frame); sport->dn_port = dn_port; sport->options = options; /* We always need buffer for put_frame */ sport->put_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info)); /* See if we need buffer for get_frame */ if (PJMEDIA_PIA_CCNT(&dn_port->info) > channel_count) { sport->get_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info)); } /* Media port interface */ sport->base.get_frame = &stereo_get_frame; sport->base.put_frame = &stereo_put_frame; sport->base.on_destroy = &stereo_destroy; /* Done */ *p_port = &sport->base; return PJ_SUCCESS; }
int main(int argc, char *argv[]) { pj_caching_pool cp; pj_pool_t *pool; pjmedia_conf *conf; pjmedia_port *player_port1, *player_port2, *writer_port; pjmedia_master_port *master_port; unsigned slot1, slot2; pj_status_t status; char tmp[10]; slot1 = 1; slot2 = 2; pj_ssize_t len; if (argc < 5) { printf("Usage: %s <clock_rate> <player file 1> <player file 2> <mix file>\n", argv[0]); exit(-1); } unsigned clock_rate = atoi(argv[1]); // Initialialize pj_init() ; pj_caching_pool_init(&cp, NULL, 0); pool = pj_pool_create(&cp.factory, "pool", 1000, 1000, NULL); // Create player port pjmedia_wav_player_port_create(pool, argv[2], 20, PJMEDIA_FILE_NO_LOOP, 0, &player_port1); pjmedia_wav_player_port_create(pool, argv[3], 20, PJMEDIA_FILE_NO_LOOP, 0, &player_port2); // Create the bridge pjmedia_conf_create(pool, MAX_WAV+4, clock_rate, 1, clock_rate * PTIME / 1000, 16, PJMEDIA_CONF_NO_DEVICE, &conf); pjmedia_conf_add_port(conf, pool, player_port2, NULL, &slot2); pjmedia_conf_add_port(conf, pool, player_port1, NULL, &slot1); pjmedia_conf_connect_port(conf, slot1, 0, 0); pjmedia_conf_connect_port(conf, slot2, 0, 0); pjmedia_port *port = pjmedia_conf_get_master_port(conf); //Both ports MUST have equal clock rate, samples per frame and channel count pjmedia_wav_writer_port_create(pool, argv[4], PJMEDIA_PIA_SRATE(&port->info), PJMEDIA_PIA_CCNT(&port->info), PJMEDIA_PIA_SPF(&port->info), PJMEDIA_PIA_BITS(&port->info), 0, 0, &writer_port); pjmedia_master_port_create(pool, port, writer_port, 0, &master_port); if (status != PJ_SUCCESS) { return 1; } status = pjmedia_master_port_start(master_port); pj_thread_sleep(5000); /* Shutdown everything */ pjmedia_master_port_destroy(master_port, PJ_TRUE); pj_pool_release(pool); pj_caching_pool_destroy(&cp); pj_shutdown(); return 0; }
/* * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *wav_play; pjmedia_port *wav_rec; pjmedia_port *wav_out; pj_status_t status; pjmedia_echo_state *ec; pjmedia_frame play_frame, rec_frame; unsigned opt = 0; unsigned latency_ms = 25; unsigned tail_ms = TAIL_LENGTH; pj_timestamp t0, t1; int i, repeat=1, interactive=0, c; pj_optind = 0; while ((c=pj_getopt(argc, argv, "d:l:a:r:i")) !=-1) { switch (c) { case 'd': latency_ms = atoi(pj_optarg); if (latency_ms < 25) { puts("Invalid delay"); puts(desc); } break; case 'l': tail_ms = atoi(pj_optarg); break; case 'a': { int alg = atoi(pj_optarg); switch (alg) { case 0: opt = 0; case 1: opt = PJMEDIA_ECHO_SPEEX; break; case 3: opt = PJMEDIA_ECHO_SIMPLE; break; default: puts("Invalid algorithm"); puts(desc); return 1; } } break; case 'r': repeat = atoi(pj_optarg); if (repeat < 1) { puts("Invalid repeat count"); puts(desc); return 1; } break; case 'i': interactive = 1; break; } } if (argc - pj_optind != 3) { puts("Error: missing argument(s)"); puts(desc); return 1; } /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for our file player */ pool = pj_pool_create( &cp.factory, /* pool factory */ "wav", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); /* Open wav_play */ status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME, PJMEDIA_FILE_NO_LOOP, 0, &wav_play); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening playback WAV file", status); return 1; } /* Open recorded wav */ status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME, PJMEDIA_FILE_NO_LOOP, 0, &wav_rec); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening recorded WAV file", status); return 1; } /* play and rec WAVs must have the same clock rate */ if (PJMEDIA_PIA_SRATE(&wav_play->info) != PJMEDIA_PIA_SRATE(&wav_rec->info)) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* .. and channel count */ if (PJMEDIA_PIA_CCNT(&wav_play->info) != PJMEDIA_PIA_CCNT(&wav_rec->info)) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* Create output wav */ status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2], PJMEDIA_PIA_SRATE(&wav_play->info), PJMEDIA_PIA_CCNT(&wav_play->info), PJMEDIA_PIA_SPF(&wav_play->info), PJMEDIA_PIA_BITS(&wav_play->info), 0, 0, &wav_out); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening output WAV file", status); return 1; } /* Create echo canceller */ status = pjmedia_echo_create2(pool, PJMEDIA_PIA_SRATE(&wav_play->info), PJMEDIA_PIA_CCNT(&wav_play->info), PJMEDIA_PIA_SPF(&wav_play->info), tail_ms, latency_ms, opt, &ec); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating EC", status); return 1; } /* Processing loop */ play_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1); rec_frame.buf = pj_pool_alloc(pool, PJMEDIA_PIA_SPF(&wav_play->info)<<1); pj_get_timestamp(&t0); for (i=0; i < repeat; ++i) { for (;;) { play_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1; status = pjmedia_port_get_frame(wav_play, &play_frame); if (status != PJ_SUCCESS) break; status = pjmedia_echo_playback(ec, (short*)play_frame.buf); rec_frame.size = PJMEDIA_PIA_SPF(&wav_play->info) << 1; status = pjmedia_port_get_frame(wav_rec, &rec_frame); if (status != PJ_SUCCESS) break; status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0); //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf, // (short*)play_frame.buf, 0, NULL); pjmedia_port_put_frame(wav_out, &rec_frame); } pjmedia_wav_player_port_set_pos(wav_play, 0); pjmedia_wav_player_port_set_pos(wav_rec, 0); } pj_get_timestamp(&t1); i = (int)pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 / (PJMEDIA_PIA_SRATE(&wav_out->info) * PJMEDIA_PIA_CCNT(&wav_out->info)); PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio", i / 1000, i % 1000)); PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1))); /* Destroy file port(s) */ status = pjmedia_port_destroy( wav_play ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_port_destroy( wav_rec ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_port_destroy( wav_out ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Destroy ec */ pjmedia_echo_destroy(ec); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); if (interactive) { char s[10], *dummy; puts("ENTER to quit"); dummy = fgets(s, sizeof(s), stdin); } /* Done. */ return 0; }
/* * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *file_port; pjmedia_snd_port *snd_port; char tmp[10]; pj_status_t status; if (argc != 2) { puts("Error: filename required"); puts(desc); return 1; } /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for our file player */ pool = pj_pool_create( &cp.factory, /* pool factory */ "wav", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); /* Create file media port from the WAV file */ status = pjmedia_wav_player_port_create( pool, /* memory pool */ argv[1], /* file to play */ 20, /* ptime. */ 0, /* flags */ 0, /* default buffer */ &file_port/* returned port */ ); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use WAV file", status); return 1; } /* Create sound player port. */ status = pjmedia_snd_port_create_player( pool, /* pool */ -1, /* use default dev. */ PJMEDIA_PIA_SRATE(&file_port->info),/* clock rate. */ PJMEDIA_PIA_CCNT(&file_port->info),/* # of channels. */ PJMEDIA_PIA_SPF(&file_port->info), /* samples per frame. */ PJMEDIA_PIA_BITS(&file_port->info),/* bits per sample. */ 0, /* options */ &snd_port /* returned port */ ); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to open sound device", status); return 1; } /* Connect file port to the sound player. * Stream playing will commence immediately. */ status = pjmedia_snd_port_connect( snd_port, file_port); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* * File should be playing and looping now, using sound device's thread. */ /* Sleep to allow log messages to flush */ pj_thread_sleep(100); printf("Playing %s..\n", argv[1]); puts(""); puts("Press <ENTER> to stop playing and quit"); if (fgets(tmp, sizeof(tmp), stdin) == NULL) { puts("EOF while reading stdin, will quit now.."); } /* Start deinitialization: */ /* Disconnect sound port from file port */ status = pjmedia_snd_port_disconnect(snd_port); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Without this sleep, Windows/DirectSound will repeteadly * play the last frame during destroy. */ pj_thread_sleep(100); /* Destroy sound device */ status = pjmedia_snd_port_destroy( snd_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Destroy file port */ status = pjmedia_port_destroy( file_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); /* Done. */ return 0; }
/* * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *rec_file_port = NULL, *play_file_port = NULL; pjmedia_master_port *master_port = NULL; pjmedia_snd_port *snd_port = NULL; pjmedia_stream *stream = NULL; pjmedia_port *stream_port; char tmp[10]; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP variables */ pj_bool_t use_srtp = PJ_FALSE; char tmp_tx_key[64]; char tmp_rx_key[64]; pj_str_t srtp_tx_key = {NULL, 0}; pj_str_t srtp_rx_key = {NULL, 0}; pj_str_t srtp_crypto_suite = {NULL, 0}; int tmp_key_len; #endif /* Default values */ const pjmedia_codec_info *codec_info; pjmedia_codec_param codec_param; pjmedia_dir dir = PJMEDIA_DIR_DECODING; pj_sockaddr_in remote_addr; pj_uint16_t local_port = 4000; char *codec_id = NULL; char *rec_file = NULL; char *play_file = NULL; enum { OPT_CODEC = 'c', OPT_LOCAL_PORT = 'p', OPT_REMOTE = 'r', OPT_PLAY_FILE = 'w', OPT_RECORD_FILE = 'R', OPT_SEND_RECV = 'b', OPT_SEND_ONLY = 's', OPT_RECV_ONLY = 'i', #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) OPT_USE_SRTP = 'S', #endif OPT_SRTP_TX_KEY = 'x', OPT_SRTP_RX_KEY = 'y', OPT_HELP = 'h', }; struct pj_getopt_option long_options[] = { { "codec", 1, 0, OPT_CODEC }, { "local-port", 1, 0, OPT_LOCAL_PORT }, { "remote", 1, 0, OPT_REMOTE }, { "play-file", 1, 0, OPT_PLAY_FILE }, { "record-file", 1, 0, OPT_RECORD_FILE }, { "send-recv", 0, 0, OPT_SEND_RECV }, { "send-only", 0, 0, OPT_SEND_ONLY }, { "recv-only", 0, 0, OPT_RECV_ONLY }, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) { "use-srtp", 2, 0, OPT_USE_SRTP }, { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY }, { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY }, #endif { "help", 0, 0, OPT_HELP }, { NULL, 0, 0, 0 }, }; int c; int option_index; pj_bzero(&remote_addr, sizeof(remote_addr)); /* init PJLIB : */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Parse arguments */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) { switch (c) { case OPT_CODEC: codec_id = pj_optarg; break; case OPT_LOCAL_PORT: local_port = (pj_uint16_t) atoi(pj_optarg); if (local_port < 1) { printf("Error: invalid local port %s\n", pj_optarg); return 1; } break; case OPT_REMOTE: { pj_str_t ip = pj_str(strtok(pj_optarg, ":")); pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":")); status = pj_sockaddr_in_init(&remote_addr, &ip, port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Invalid remote address", status); return 1; } } break; case OPT_PLAY_FILE: play_file = pj_optarg; break; case OPT_RECORD_FILE: rec_file = pj_optarg; break; case OPT_SEND_RECV: dir = PJMEDIA_DIR_ENCODING_DECODING; break; case OPT_SEND_ONLY: dir = PJMEDIA_DIR_ENCODING; break; case OPT_RECV_ONLY: dir = PJMEDIA_DIR_DECODING; break; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) case OPT_USE_SRTP: use_srtp = PJ_TRUE; if (pj_optarg) { pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg)); } else { srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80"); } break; case OPT_SRTP_TX_KEY: tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, (int)strlen(pj_optarg)); pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2); break; case OPT_SRTP_RX_KEY: tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, (int)strlen(pj_optarg)); pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2); break; #endif case OPT_HELP: usage(); return 1; default: printf("Invalid options %s\n", argv[pj_optind]); return 1; } } /* Verify arguments. */ if (dir & PJMEDIA_DIR_ENCODING) { if (remote_addr.sin_addr.s_addr == 0) { printf("Error: remote address must be set\n"); return 1; } } if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) { printf("Direction is set to --send-only because of --play-file\n"); dir = PJMEDIA_DIR_ENCODING; } #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP validation */ if (use_srtp) { if (!srtp_tx_key.slen || !srtp_rx_key.slen) { printf("Error: Key for each SRTP stream direction must be set\n"); return 1; } } #endif /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for application purpose */ pool = pj_pool_create( &cp.factory, /* pool factory */ "app", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); /* Register all supported codecs */ status = init_codecs(med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Find which codec to use. */ if (codec_id) { unsigned count = 1; pj_str_t str_codec_id = pj_str(codec_id); pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt); status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr, &str_codec_id, &count, &codec_info, NULL); if (status != PJ_SUCCESS) { printf("Error: unable to find codec %s\n", codec_id); return 1; } } else { /* Default to pcmu */ pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt), 0, &codec_info); } /* Create stream based on program arguments */ status = create_stream(pool, med_endpt, codec_info, dir, local_port, &remote_addr, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) use_srtp, &srtp_crypto_suite, &srtp_tx_key, &srtp_rx_key, #endif &stream); if (status != PJ_SUCCESS) goto on_exit; /* Get codec default param for info */ status = pjmedia_codec_mgr_get_default_param( pjmedia_endpt_get_codec_mgr(med_endpt), codec_info, &codec_param); /* Should be ok, as create_stream() above succeeded */ pj_assert(status == PJ_SUCCESS); /* Get the port interface of the stream */ status = pjmedia_stream_get_port( stream, &stream_port); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); if (play_file) { unsigned wav_ptime; wav_ptime = PJMEDIA_PIA_PTIME(&stream_port->info); status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime, 0, -1, &play_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); goto on_exit; } status = pjmedia_master_port_create(pool, play_file_port, stream_port, 0, &master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create master port", status); goto on_exit; } status = pjmedia_master_port_start(master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error starting master port", status); goto on_exit; } printf("Playing from WAV file %s..\n", play_file); } else if (rec_file) { status = pjmedia_wav_writer_port_create(pool, rec_file, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, 0, &rec_file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to use file", status); goto on_exit; } status = pjmedia_master_port_create(pool, stream_port, rec_file_port, 0, &master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create master port", status); goto on_exit; } status = pjmedia_master_port_start(master_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error starting master port", status); goto on_exit; } printf("Recording to WAV file %s..\n", rec_file); } else { /* Create sound device port. */ if (dir == PJMEDIA_DIR_ENCODING_DECODING) status = pjmedia_snd_port_create(pool, -1, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else if (dir == PJMEDIA_DIR_ENCODING) status = pjmedia_snd_port_create_rec(pool, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); else status = pjmedia_snd_port_create_player(pool, -1, PJMEDIA_PIA_SRATE(&stream_port->info), PJMEDIA_PIA_CCNT(&stream_port->info), PJMEDIA_PIA_SPF(&stream_port->info), PJMEDIA_PIA_BITS(&stream_port->info), 0, &snd_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create sound port", status); goto on_exit; } /* Connect sound port to stream */ status = pjmedia_snd_port_connect( snd_port, stream_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* Start streaming */ pjmedia_stream_start(stream); /* Done */ if (dir == PJMEDIA_DIR_DECODING) printf("Stream is active, dir is recv-only, local port is %d\n", local_port); else if (dir == PJMEDIA_DIR_ENCODING) printf("Stream is active, dir is send-only, sending to %s:%d\n", pj_inet_ntoa(remote_addr.sin_addr), pj_ntohs(remote_addr.sin_port)); else printf("Stream is active, send/recv, local port is %d, " "sending to %s:%d\n", local_port, pj_inet_ntoa(remote_addr.sin_addr), pj_ntohs(remote_addr.sin_port)); for (;;) { puts(""); puts("Commands:"); puts(" s Display media statistics"); puts(" q Quit"); puts(""); printf("Command: "); fflush(stdout); if (fgets(tmp, sizeof(tmp), stdin) == NULL) { puts("EOF while reading stdin, will quit now.."); break; } if (tmp[0] == 's') print_stream_stat(stream, &codec_param); else if (tmp[0] == 'q') break; } /* Start deinitialization: */ on_exit: /* Destroy sound device */ if (snd_port) { pjmedia_snd_port_destroy( snd_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); } /* If there is master port, then we just need to destroy master port * (it will recursively destroy upstream and downstream ports, which * in this case are file_port and stream_port). */ if (master_port) { pjmedia_master_port_destroy(master_port, PJ_TRUE); play_file_port = NULL; stream = NULL; } /* Destroy stream */ if (stream) { pjmedia_transport *tp; tp = pjmedia_stream_get_transport(stream); pjmedia_stream_destroy(stream); pjmedia_transport_close(tp); } /* Destroy file ports */ if (play_file_port) pjmedia_port_destroy( play_file_port ); if (rec_file_port) pjmedia_port_destroy( rec_file_port ); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); return (status == PJ_SUCCESS) ? 0 : 1; }
int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *file_port = NULL; pjmedia_port *stereo_port = NULL; pjmedia_snd_port *snd_port = NULL; int dev_id = -1; char tmp[10]; pj_status_t status; char *wav_file = NULL; unsigned mode = 0; unsigned rec_ch_cnt = 1; unsigned snd_ch_cnt = 2; enum { OPT_MODE = 'm', OPT_REC_CHANNEL = 'C', OPT_SND_CHANNEL = 'c', }; struct pj_getopt_option long_options[] = { { "mode", 1, 0, OPT_MODE }, { "rec-ch-cnt", 1, 0, OPT_REC_CHANNEL }, { "snd-ch-cnt", 1, 0, OPT_SND_CHANNEL }, { NULL, 0, 0, 0 }, }; int c; int option_index; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Parse arguments */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "m:C:c:", long_options, &option_index))!=-1) { switch (c) { case OPT_MODE: if (mode) { app_perror(THIS_FILE, "Cannot record and play at once!", PJ_EINVAL); return 1; } mode = atoi(pj_optarg); break; case OPT_REC_CHANNEL: rec_ch_cnt = atoi(pj_optarg); break; case OPT_SND_CHANNEL: snd_ch_cnt = atoi(pj_optarg); break; default: printf("Invalid options %s\n", argv[pj_optind]); puts(desc); return 1; } } wav_file = argv[pj_optind]; /* Verify arguments. */ if (!wav_file) { app_perror(THIS_FILE, "WAV file not specified!", PJ_EINVAL); puts(desc); return 1; } if (!snd_ch_cnt || !rec_ch_cnt || rec_ch_cnt > 6) { app_perror(THIS_FILE, "Invalid or too many channel count!", PJ_EINVAL); puts(desc); return 1; } if (mode != MODE_RECORD && mode != MODE_PLAY) { app_perror(THIS_FILE, "Invalid operation mode!", PJ_EINVAL); puts(desc); return 1; } /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for our file player */ pool = pj_pool_create( &cp.factory, /* pool factory */ "app", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); if (mode == MODE_PLAY) { /* Create WAVE file player port. */ status = pjmedia_wav_player_port_create( pool, wav_file, PTIME, 0, 0, &file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to open file", status); return 1; } /* Create sound player port. */ status = pjmedia_snd_port_create_player( pool, /* pool */ dev_id, /* device id. */ PJMEDIA_PIA_SRATE(&file_port->info),/* clock rate. */ snd_ch_cnt, /* # of channels. */ snd_ch_cnt * PTIME * /* samples per frame. */ PJMEDIA_PIA_SRATE(&file_port->info) / 1000, PJMEDIA_PIA_BITS(&file_port->info),/* bits per sample. */ 0, /* options */ 15, 5, &snd_port /* returned port */ ); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to open sound device", status); return 1; } if (snd_ch_cnt != PJMEDIA_PIA_CCNT(&file_port->info)) { status = pjmedia_stereo_port_create( pool, file_port, snd_ch_cnt, 0, &stereo_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create stereo port", status); return 1; } status = pjmedia_snd_port_connect(snd_port, stereo_port); } else { status = pjmedia_snd_port_connect(snd_port, file_port); } if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to connect sound port", status); return 1; } } else { /* Create WAVE file writer port. */ status = pjmedia_wav_writer_port_create(pool, wav_file, REC_CLOCK_RATE, rec_ch_cnt, rec_ch_cnt * PTIME * REC_CLOCK_RATE / 1000, NBITS, 0, 0, &file_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to open file", status); return 1; } /* Create sound player port. */ status = pjmedia_snd_port_create_rec( pool, /* pool */ dev_id, /* device id. */ REC_CLOCK_RATE, /* clock rate. */ snd_ch_cnt, /* # of channels. */ snd_ch_cnt * PTIME * REC_CLOCK_RATE / 1000, /* samples per frame. */ NBITS, /* bits per sample. */ 0, /* options */ 15, 5, &snd_port /* returned port */ ); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to open sound device", status); return 1; } if (rec_ch_cnt != snd_ch_cnt) { status = pjmedia_stereo_port_create( pool, file_port, snd_ch_cnt, 0, &stereo_port); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create stereo port", status); return 1; } status = pjmedia_snd_port_connect(snd_port, stereo_port); } else { status = pjmedia_snd_port_connect(snd_port, file_port); } if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to connect sound port", status); return 1; } } /* Dump memory usage */ dump_pool_usage(THIS_FILE, &cp); /* * File should be playing and looping now, using sound device's thread. */ /* Sleep to allow log messages to flush */ pj_thread_sleep(100); printf("Mode = %s\n", (mode == MODE_PLAY? "playing" : "recording") ); printf("File port channel count = %d\n", PJMEDIA_PIA_CCNT(&file_port->info)); printf("Sound port channel count = %d\n", PJMEDIA_PIA_CCNT(&pjmedia_snd_port_get_port(snd_port)->info)); puts(""); puts("Press <ENTER> to stop and quit"); if (fgets(tmp, sizeof(tmp), stdin) == NULL) { puts("EOF while reading stdin, will quit now.."); } /* Start deinitialization: */ /* Destroy sound device */ status = pjmedia_snd_port_destroy( snd_port ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Destroy stereo port and file_port. * Stereo port will destroy all downstream ports (e.g. the file port) */ status = pjmedia_port_destroy( stereo_port? stereo_port : file_port); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); /* Done. */ return 0; }
/* * "Write" a multichannel frame downstream. This would split * the multichannel frame into individual mono channel, and write * it to the appropriate port. */ static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct splitcomb *sc = (struct splitcomb*) this_port; unsigned ch; /* Handle null frame */ if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { pjmedia_port *port = sc->port_desc[ch].port; if (!port) continue; if (!sc->port_desc[ch].reversed) { pjmedia_port_put_frame(port, frame); } else { struct reverse_port *rport = (struct reverse_port*)port; /* Update the number of NULL frames received. Once we have too * many of this, we'll stop calling op_update() to let the * media be suspended. */ if (++rport->buf[DIR_DOWNSTREAM].null_cnt > rport->max_null_frames) { /* Prevent the counter from overflowing and resetting * back to zero */ rport->buf[DIR_DOWNSTREAM].null_cnt = rport->max_null_frames + 1; continue; } /* Write zero port to delaybuf so that it doesn't underflow. * If we don't do this, get_frame() on this direction will * cause delaybuf to generate missing frame and the last * frame transmitted to delaybuf will be replayed multiple * times, which doesn't sound good. */ /* Update rport state. */ op_update(rport, DIR_DOWNSTREAM, OP_PUT); /* Discard frame if rport is paused on this direction */ if (rport->buf[DIR_DOWNSTREAM].paused) continue; /* Generate zero frame. */ pjmedia_zero_samples(sc->put_buf, PJMEDIA_PIA_SPF(&port->info)); /* Put frame to delay buffer */ pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf, sc->put_buf); } } return PJ_SUCCESS; } /* Not sure how we would handle partial frame, so better reject * it for now. */ PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info), PJ_EINVAL); /* * Write mono frame into each channels */ for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { pjmedia_port *port = sc->port_desc[ch].port; if (!port) continue; /* Extract the mono frame to temporary buffer */ extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch, PJMEDIA_PIA_CCNT(&this_port->info), (unsigned)frame->size * 8 / PJMEDIA_PIA_BITS(&this_port->info) / PJMEDIA_PIA_CCNT(&this_port->info)); if (!sc->port_desc[ch].reversed) { /* Write to normal port */ pjmedia_frame mono_frame; mono_frame.buf = sc->put_buf; mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info); mono_frame.type = frame->type; mono_frame.timestamp.u64 = frame->timestamp.u64; /* Write */ pjmedia_port_put_frame(port, &mono_frame); } else { /* Write to reversed phase port */ struct reverse_port *rport = (struct reverse_port*)port; /* Reset NULL frame counter */ rport->buf[DIR_DOWNSTREAM].null_cnt = 0; /* Update rport state. */ op_update(rport, DIR_DOWNSTREAM, OP_PUT); if (!rport->buf[DIR_DOWNSTREAM].paused) { pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf, sc->put_buf); } } } return PJ_SUCCESS; }
static pj_status_t test_init(void) { struct stream_cfg strm_cfg; pj_status_t status; /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&g_app.cp, &pj_pool_factory_default_policy, 0); /* Pool */ g_app.pool = pj_pool_create(&g_app.cp.factory, "g_app", 512, 512, NULL); /* Log file */ if (g_app.cfg.log_file) { status = pj_file_open(g_app.pool, g_app.cfg.log_file, PJ_O_WRONLY, &g_app.log_fd); if (status != PJ_SUCCESS) { jbsim_perror("Error writing output file", status); goto on_error; } pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_COLOR | PJ_LOG_HAS_LEVEL_TEXT); pj_log_set_log_func(&log_cb); } /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&g_app.cp.factory, NULL, 0, &g_app.endpt); if (status != PJ_SUCCESS) { jbsim_perror("Error creating media endpoint", status); goto on_error; } /* Register codecs */ pjmedia_codec_register_audio_codecs(g_app.endpt, NULL); /* Create the loop transport */ status = pjmedia_transport_loop_create(g_app.endpt, &g_app.loop); if (status != PJ_SUCCESS) { jbsim_perror("Error creating loop transport", status); goto on_error; } /* Create transmitter stream */ pj_bzero(&strm_cfg, sizeof(strm_cfg)); strm_cfg.name = "tx"; strm_cfg.dir = PJMEDIA_DIR_ENCODING; strm_cfg.codec = g_app.cfg.codec; strm_cfg.ptime = g_app.cfg.tx_ptime; strm_cfg.dtx = g_app.cfg.tx_dtx; strm_cfg.plc = PJ_TRUE; status = stream_init(&strm_cfg, &g_app.tx); if (status != PJ_SUCCESS) goto on_error; /* Create transmitter WAV */ status = pjmedia_wav_player_port_create(g_app.pool, g_app.cfg.tx_wav_in, g_app.cfg.tx_ptime, 0, 0, &g_app.tx_wav); if (status != PJ_SUCCESS) { jbsim_perror("Error reading input WAV file", status); goto on_error; } /* Make sure stream and WAV parameters match */ if (PJMEDIA_PIA_SRATE(&g_app.tx_wav->info) != PJMEDIA_PIA_SRATE(&g_app.tx->port->info) || PJMEDIA_PIA_CCNT(&g_app.tx_wav->info) != PJMEDIA_PIA_CCNT(&g_app.tx->port->info)) { jbsim_perror("Error: Input WAV file has different clock rate " "or number of channels than the codec", PJ_SUCCESS); goto on_error; } /* Create receiver */ pj_bzero(&strm_cfg, sizeof(strm_cfg)); strm_cfg.name = "rx"; strm_cfg.dir = PJMEDIA_DIR_DECODING; strm_cfg.codec = g_app.cfg.codec; strm_cfg.ptime = g_app.cfg.rx_ptime; strm_cfg.dtx = PJ_TRUE; strm_cfg.plc = g_app.cfg.rx_plc; status = stream_init(&strm_cfg, &g_app.rx); if (status != PJ_SUCCESS) goto on_error; /* Create receiver WAV */ status = pjmedia_wav_writer_port_create(g_app.pool, g_app.cfg.rx_wav_out, PJMEDIA_PIA_SRATE(&g_app.rx->port->info), PJMEDIA_PIA_CCNT(&g_app.rx->port->info), PJMEDIA_PIA_SPF(&g_app.rx->port->info), PJMEDIA_PIA_BITS(&g_app.rx->port->info), 0, 0, &g_app.rx_wav); if (status != PJ_SUCCESS) { jbsim_perror("Error creating output WAV file", status); goto on_error; } /* Frame buffer */ g_app.framebuf = (pj_int16_t*) pj_pool_alloc(g_app.pool, MAX(PJMEDIA_PIA_SPF(&g_app.rx->port->info), PJMEDIA_PIA_SPF(&g_app.tx->port->info)) * sizeof(pj_int16_t)); /* Set the receiver in the loop transport */ pjmedia_transport_loop_disable_rx(g_app.loop, g_app.tx->strm, PJ_TRUE); /* Done */ return PJ_SUCCESS; on_error: test_destroy(); return status; }