static void clock_cb(const pj_timestamp *ts, void *user_data) { play_file_data *play_file = (play_file_data*)user_data; pjmedia_frame read_frame, write_frame; pj_status_t status; PJ_UNUSED_ARG(ts); /* Read frame from file */ read_frame.buf = play_file->read_buf; read_frame.size = play_file->read_buf_size; pjmedia_port_get_frame(play_file->play_port, &read_frame); /* Decode frame, if needed */ if (play_file->decoder) { pjmedia_vid_codec *decoder = play_file->decoder; write_frame.buf = play_file->dec_buf; write_frame.size = play_file->dec_buf_size; status = pjmedia_vid_codec_decode(decoder, 1, &read_frame, write_frame.size, &write_frame); if (status != PJ_SUCCESS) return; } else { write_frame = read_frame; } /* Display frame locally */ if (play_file->renderer) pjmedia_port_put_frame(play_file->renderer, &write_frame); /* Send frame */ pjmedia_port_put_frame(play_file->stream_port, &write_frame); }
static pj_status_t plc_put_frame( pjmedia_port *this_port, const pjmedia_frame *frame) { pj_status_t status; int i; struct plc_port *plcp = (struct plc_port*)this_port; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); PJ_LOG(6, (THIS_FILE, "packet: sz=%d ts=%llu", frame->size/sizeof(pj_uint16_t), frame->timestamp.u64)); if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) { em_plc_mode mode = plcp->frame.type == PJMEDIA_FRAME_TYPE_NONE ? \ EM_PLC_EMPTY : plcp->plc_mode; switch (mode) { case EM_PLC_SMART: for (i=0; i<plcp->fpp; i++){ status = plcp->codec->op->recover(plcp->codec, BUF_SIZE, &plcp->frame); plcp->frame.timestamp.u64 = 0; if (status != PJ_SUCCESS) return status; status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame); if (status != PJ_SUCCESS) return status; } break; case EM_PLC_REPEAT: for (i=0; i<plcp->fpp; i++){ plcp->frame.timestamp.u64 = 0; status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame); if (status != PJ_SUCCESS) return status; } break; case EM_PLC_NOISE: for (i=0; i<plcp->fpp; i++){ int j=0; plcp->frame.size = plcp->dn_port->info.bytes_per_frame; plcp->frame.type = PJMEDIA_FRAME_TYPE_AUDIO; plcp->frame.timestamp.u64 = 0; for (j=0; j<plcp->frame.size; j++) ((char*)plcp->frame.buf)[j] = ((char)pj_rand()) >> 5; status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame); if (status != PJ_SUCCESS) return status; } default: for (i=0; i<plcp->fpp; i++){ plcp->frame.size = plcp->dn_port->info.bytes_per_frame; plcp->frame.type = PJMEDIA_FRAME_TYPE_AUDIO; plcp->frame.timestamp.u64 = 0; pj_bzero(plcp->frame.buf, plcp->frame.size); status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame); } } plcp->stats.lost++; } else {
/* * The callback called by sound recorder when it has finished capturing a * frame. */ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); port = snd_port->port; if (port == NULL) return PJ_SUCCESS; /* Cancel echo */ if (snd_port->ec_state && !snd_port->ec_suspended) { pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0); } #ifdef MY_SAVE_FILE_BEFORE_SPEEX fwrite(frame->buf,1,frame->size,fd_bfspeex); #endif if (pjmedia_audio_use_speex_ns == PJ_TRUE){ speex_preprocess_run(snd_port->speex_st, frame->buf); //PJ_LOG(5,(THIS_FILE, "frame->size:%d",frame->size)); } #ifdef MY_SAVE_FILE_SEND fwrite(frame->buf,1,frame->size,fd_save); #endif pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; }
static pj_status_t resample_put_frame(pjmedia_port *this_port, const pjmedia_frame *frame) { struct resample_port *rport = (struct resample_port*) this_port; pjmedia_frame downstream_frame; /* Return if we don't have downstream port. */ if (rport->dn_port == NULL) { return PJ_SUCCESS; } if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { pjmedia_resample_run( rport->resample_put, (const pj_int16_t*) frame->buf, rport->put_buf); downstream_frame.buf = rport->put_buf; downstream_frame.size = rport->dn_port->info.bytes_per_frame; } else { downstream_frame.buf = frame->buf; downstream_frame.size = frame->size; } downstream_frame.type = frame->type; downstream_frame.timestamp.u64 = frame->timestamp.u64; return pjmedia_port_put_frame( rport->dn_port, &downstream_frame ); }
void pjs_audio_combine::media_sync(pjs_media_sync_param_t param) { if (m_ready) { switch (param) { case PJMS_SYNC_BEFORE: { if (!m_oneway && m_putbuffer) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_NONE; frame.buf = m_putbuffer; frame.size = m_buffer_size; get_frame(&frame); if (frame.type != PJMEDIA_FRAME_TYPE_NONE) pjmedia_port_put_frame(m_combiner, &frame); } break; } case PJMS_SYNC_AFTER: { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_NONE; frame.buf = m_getbuffer; frame.size = m_buffer_size; if (pjmedia_port_get_frame(m_combiner, &frame) == PJ_SUCCESS) put_frame(&frame); break; } } } }
/* * The callback called by sound recorder when it has finished capturing a * frame. */ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); /* Invoke preview callback */ if (snd_port->on_rec_frame) (*snd_port->on_rec_frame)(snd_port->user_data, frame); port = snd_port->port; if (port == NULL) return PJ_SUCCESS; /* Cancel echo */ if (snd_port->ec_state && !snd_port->ec_suspended) { pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0); } pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; }
static void run_one_frame(pjmedia_port *src, pjmedia_port *dst, pj_bool_t *has_frame) { pjmedia_frame frame; pj_status_t status; pj_bzero(&frame, sizeof(frame)); frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = g_app.framebuf; frame.size = PJMEDIA_PIA_SPF(&dst->info) * 2; status = pjmedia_port_get_frame(src, &frame); pj_assert(status == PJ_SUCCESS); if (status!= PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { frame.buf = g_app.framebuf; pjmedia_zero_samples(g_app.framebuf, PJMEDIA_PIA_SPF(&src->info)); frame.size = PJMEDIA_PIA_SPF(&src->info) * 2; if (has_frame) *has_frame = PJ_FALSE; } else { if (has_frame) *has_frame = PJ_TRUE; } status = pjmedia_port_put_frame(dst, &frame); pj_assert(status == PJ_SUCCESS); }
static pj_status_t ec_put_frame( pjmedia_port *this_port, pjmedia_frame *frame) { struct ec *ec = (struct ec*)this_port; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) { return pjmedia_port_put_frame(ec->dn_port, frame); } PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info), PJ_EINVAL); pjmedia_echo_capture(ec->ec, (pj_int16_t*)frame->buf, 0); return pjmedia_port_put_frame(ec->dn_port, frame); }
static pj_status_t ec_put_frame( pjmedia_port *this_port, const pjmedia_frame *frame) { struct ec *ec = (struct ec*)this_port; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) { return pjmedia_port_put_frame(ec->dn_port, frame); } PJ_ASSERT_RETURN(frame->size == this_port->info.samples_per_frame * 2, PJ_EINVAL); pjmedia_echo_capture(ec->ec, frame->buf, 0); return pjmedia_port_put_frame(ec->dn_port, frame); }
/* * The callback called by sound recorder when it has finished capturing a * frame. This version is for non-PCM data. */ static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; port = snd_port->port; if (port == NULL) return PJ_SUCCESS; pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; }
/* * Callback to be called for each clock ticks. */ static void clock_callback(const pj_timestamp *ts, void *user_data) { pjmedia_master_port *m = (pjmedia_master_port*) user_data; pjmedia_frame frame; pj_status_t status; /* Lock access to ports. */ pj_lock_acquire(m->lock); /* Get frame from upstream port and pass it to downstream port */ pj_bzero(&frame, sizeof(frame)); frame.buf = m->buff; frame.size = m->buff_size; frame.timestamp.u64 = ts->u64; status = pjmedia_port_get_frame(m->u_port, &frame); if (status != PJ_SUCCESS) frame.type = PJMEDIA_FRAME_TYPE_NONE; status = pjmedia_port_put_frame(m->d_port, &frame); /* Get frame from downstream port and pass it to upstream port */ pj_bzero(&frame, sizeof(frame)); frame.buf = m->buff; frame.size = m->buff_size; frame.timestamp.u64 = ts->u64; status = pjmedia_port_get_frame(m->d_port, &frame); if (status != PJ_SUCCESS) frame.type = PJMEDIA_FRAME_TYPE_NONE; status = pjmedia_port_put_frame(m->u_port, &frame); /* Release lock */ pj_lock_release(m->lock); }
/* * The callback called by sound recorder when it has finished capturing a * frame. This version is for non-PCM data. */ static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; /* Invoke preview callback */ if (snd_port->on_rec_frame) (*snd_port->on_rec_frame)(snd_port->user_data, frame); port = snd_port->port; if (port == NULL) return PJ_SUCCESS; pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; }
/* * The callback called by sound recorder when it has finished capturing a * frame. */ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; port = snd_port->port; if (port == NULL) return PJ_SUCCESS; /* Cancel echo */ if (snd_port->ec_state && !snd_port->ec_suspended) { pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0); } pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; }
static pj_status_t stereo_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct stereo_port *sport = (struct stereo_port*) this_port; const pjmedia_audio_format_detail *s_afd, *dn_afd; pjmedia_frame tmp_frame; /* Return if we don't have downstream port. */ if (sport->dn_port == NULL) { return PJ_SUCCESS; } s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1); dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt, 1); if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { tmp_frame.buf = sport->put_buf; if (dn_afd->channel_count == 1) { pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf, (const pj_int16_t*)frame->buf, s_afd->channel_count, PJMEDIA_AFD_SPF(s_afd), (sport->options & PJMEDIA_STEREO_MIX), 0); } else { pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf, (const pj_int16_t*)frame->buf, dn_afd->channel_count, PJMEDIA_AFD_SPF(s_afd), sport->options); } tmp_frame.size = PJMEDIA_AFD_AVG_FSZ(dn_afd); } else { tmp_frame.buf = frame->buf; tmp_frame.size = frame->size; } tmp_frame.type = frame->type; tmp_frame.timestamp.u64 = frame->timestamp.u64; return pjmedia_port_put_frame( sport->dn_port, &tmp_frame ); }
static pj_status_t stereo_put_frame(pjmedia_port *this_port, const pjmedia_frame *frame) { struct stereo_port *sport = (struct stereo_port*) this_port; pjmedia_frame tmp_frame; /* Return if we don't have downstream port. */ if (sport->dn_port == NULL) { return PJ_SUCCESS; } if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { tmp_frame.buf = sport->put_buf; if (sport->dn_port->info.channel_count == 1) { pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf, (const pj_int16_t*)frame->buf, sport->base.info.channel_count, sport->base.info.samples_per_frame, (sport->options & PJMEDIA_STEREO_MIX), 0); } else { pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf, (const pj_int16_t*)frame->buf, sport->dn_port->info.channel_count, sport->base.info.samples_per_frame, sport->options); } tmp_frame.size = sport->dn_port->info.bytes_per_frame; } else { tmp_frame.buf = frame->buf; tmp_frame.size = frame->size; } tmp_frame.type = frame->type; tmp_frame.timestamp.u64 = frame->timestamp.u64; return pjmedia_port_put_frame( sport->dn_port, &tmp_frame ); }
static pj_status_t lb_push_frame(struct leaky_bucket_port *lb) { struct leaky_bucket_item *fst = lb->first_item; pj_status_t status; if (fst == NULL){ return PJ_SUCCESS; } PJ_LOG(6, (THIS_FILE, "push frame to dn port: sz=%u, ts=%llu", fst->frame.size/sizeof(pj_uint16_t), fst->frame.timestamp.u64)); status = pjmedia_port_put_frame(lb->dn_port, &fst->frame); if (status != PJ_SUCCESS) return status; if (fst->frame.type == PJMEDIA_FRAME_TYPE_AUDIO){ lb->items --; } else { } if (fst->next == fst) { lb->first_item = lb->last_item = NULL; } else { lb->first_item = fst->next; pj_list_erase(fst); } return PJ_SUCCESS; }
static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame) { return pjmedia_port_put_frame((pjmedia_port*)user_data, frame); }
/* * 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 (wav_play->info.clock_rate != wav_rec->info.clock_rate) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* .. and channel count */ if (wav_play->info.channel_count != wav_rec->info.channel_count) { 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], wav_play->info.clock_rate, wav_play->info.channel_count, wav_play->info.samples_per_frame, wav_play->info.bits_per_sample, 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, wav_play->info.clock_rate, wav_play->info.channel_count, wav_play->info.samples_per_frame, 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, wav_play->info.samples_per_frame<<1); rec_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); pj_get_timestamp(&t0); for (i=0; i < repeat; ++i) { for (;;) { play_frame.size = wav_play->info.samples_per_frame << 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 = wav_play->info.samples_per_frame << 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 = pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 / (wav_out->info.clock_rate * wav_out->info.channel_count); 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; }
static pj_status_t codec_put_frame(pjmedia_port *port, pjmedia_frame *frame) { enum { MAX_PACKETS = 50 }; codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata; pj_status_t status; pjmedia_vid_codec *codec = port_data->codec; unsigned enc_cnt = 0; pj_uint8_t *enc_buf; unsigned enc_size_left; pjmedia_frame enc_frames[MAX_PACKETS]; pj_bool_t has_more = PJ_FALSE; enc_buf = port_data->enc_buf; enc_size_left = port_data->enc_buf_size; /* * Encode */ enc_frames[enc_cnt].buf = enc_buf; enc_frames[enc_cnt].size = enc_size_left; status = pjmedia_vid_codec_encode_begin(codec, NULL, frame, enc_size_left, &enc_frames[enc_cnt], &has_more); if (status != PJ_SUCCESS) goto on_error; enc_buf += enc_frames[enc_cnt].size; enc_size_left -= enc_frames[enc_cnt].size; ++enc_cnt; while (has_more) { enc_frames[enc_cnt].buf = enc_buf; enc_frames[enc_cnt].size = enc_size_left; status = pjmedia_vid_codec_encode_more(codec, enc_size_left, &enc_frames[enc_cnt], &has_more); if (status != PJ_SUCCESS) break; enc_buf += enc_frames[enc_cnt].size; enc_size_left -= enc_frames[enc_cnt].size; ++enc_cnt; if (enc_cnt >= MAX_PACKETS) { assert(!"Too many packets!"); break; } } /* * Decode */ status = pjmedia_vid_codec_decode(codec, enc_cnt, enc_frames, frame->size, frame); if (status != PJ_SUCCESS) goto on_error; /* Display */ status = pjmedia_port_put_frame( pjmedia_vid_port_get_passive_port(port_data->rdr_port), frame); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: pj_perror(3, THIS_FILE, status, "codec_put_frame() error"); return status; }
static pj_status_t sp_put_frame( pjmedia_port *this_port, const pjmedia_frame *frame) { struct silence_port *sp = (struct silence_port*)this_port; unsigned frame_size = frame->size / sizeof(pj_uint16_t); unsigned samples_per_frame = sp->base.info.samples_per_frame; pj_int16_t tmp_buf[frame_size]; pjmedia_frame tmp_frame; pj_status_t status; PJ_LOG(6, (THIS_FILE, "packet: sz=%d ts=%llu", frame->size/sizeof(pj_uint16_t), frame->timestamp.u64)); PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) { PJ_LOG(5, (THIS_FILE, "empty frame passed")); return pjmedia_port_put_frame(sp->dn_port, frame); } if (sp->frames == 0) { PJ_LOG(5, (THIS_FILE, "%u: this is first received frame, " "so just update last timestamp with its " "timestamp + size: %llu + %d", sp->frames, frame->timestamp.u64, samples_per_frame)); sp->last_ts.u64 = frame->timestamp.u64 + samples_per_frame; } else if (frame->timestamp.u64 == 0){ PJ_LOG(5, (THIS_FILE, "%u: frame timestamp is unknown, so increment " "by %d (samples per frame)", sp->frames, samples_per_frame)); sp->last_ts.u64 += samples_per_frame; } else if (frame->timestamp.u64 < sp->last_ts.u64 ){ PJ_LOG(5, (THIS_FILE, "%u: received frame timestamp is lesser than " "latest timestamp (%llu < %llu)", sp->frames, frame->timestamp.u64, sp->last_ts.u64)); sp->last_ts.u64 = frame->timestamp.u64 + samples_per_frame; } else if (frame->timestamp.u64 > sp->last_ts.u64){ unsigned zero_padding_count = frame->timestamp.u64 - sp->last_ts.u64; pj_int16_t zero_buf[zero_padding_count]; PJ_LOG(5, (THIS_FILE, "%u: received frame timestamp is greater than " "latest timestamp (%llu > %llu), fill output buffer with %d " "empty frames", sp->frames, frame->timestamp.u64, sp->last_ts.u64, zero_padding_count)); pj_bzero(zero_buf, sizeof(zero_buf)); pjmedia_circ_buf_write(sp->buf, zero_buf, zero_padding_count); PJ_LOG(6, (THIS_FILE, "write in circ buf %u zeros", zero_padding_count)); sp->last_ts.u64 = frame->timestamp.u64 + samples_per_frame; } else { sp->last_ts.u64 += samples_per_frame; } pjmedia_circ_buf_write(sp->buf, (pj_uint16_t*)frame->buf, frame_size); PJ_LOG(6, (THIS_FILE, "write in circ buf %u bytes", frame_size)); pj_memcpy(&tmp_frame, frame, sizeof(pjmedia_frame)); tmp_frame.buf = (void*)tmp_buf; sp->frames ++; /* put everything saved into buffer FIXME: wrong timestamps. Fortunately, wav writer don't care about timestamps */ while (pjmedia_circ_buf_get_len(sp->buf) >= frame_size){ pjmedia_circ_buf_read(sp->buf, tmp_buf, frame_size); PJ_LOG(6, (THIS_FILE, "read from circ buf %u bytes", frame_size)); status = pjmedia_port_put_frame(sp->dn_port, &tmp_frame); if (status != PJ_SUCCESS) return status; } 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); } }
static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct bidir_port *p = (struct bidir_port*)this_port; return pjmedia_port_put_frame(p->put_port, frame); }
/* * "Write" a multichannel frame. 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, const 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 < this_port->info.channel_count; ++ch) { pjmedia_port *port = sc->port_desc[ch].port; if (!port) continue; pjmedia_port_put_frame(port, frame); } return PJ_SUCCESS; } /* Not sure how we would handle partial frame, so better reject * it for now. */ PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame, PJ_EINVAL); /* * Write mono frame into each channels */ for (ch=0; ch < this_port->info.channel_count; ++ch) { pjmedia_port *port = sc->port_desc[ch].port; if (!port) continue; if (!sc->port_desc[ch].reversed) { /* Write to normal port */ pjmedia_frame mono_frame; /* Extract the mono frame */ extract_mono_frame(frame->buf, sc->put_buf, ch, this_port->info.channel_count, frame->size * 8 / this_port->info.bits_per_sample / this_port->info.channel_count); mono_frame.buf = sc->put_buf; mono_frame.size = frame->size / this_port->info.channel_count; 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; if (rport->dn_write_pos == rport->dn_read_pos) { /* Only report overflow if the frame is constantly read * by the 'consumer' of the reverse port. * It is possible that nobody reads the buffer, so causing * overflow to happen rapidly, and writing log message this * way does not seem to be wise. */ if (rport->dn_read_pos != rport->dn_overflow_pos) { rport->dn_overflow_pos = rport->dn_read_pos; LOG_DN_((THIS_FILE, "Overflow in downstream direction")); } /* Adjust write position */ rport->dn_write_pos = (rport->dn_write_pos + rport->buf_cnt/2) % rport->buf_cnt; } /* Extract mono-frame and put it in downstream buffer */ extract_mono_frame(frame->buf, rport->dnstream_buf[rport->dn_write_pos], ch, this_port->info.channel_count, frame->size * 8 / this_port->info.bits_per_sample / this_port->info.channel_count); rport->dn_write_pos = (rport->dn_write_pos + 1) % rport->buf_cnt; } } return PJ_SUCCESS; }
/* * "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 tee_put_frame(pjmedia_port *port, pjmedia_frame *frame) { vid_tee_port *tee = (vid_tee_port*)port; unsigned i, j; const pj_uint8_t PUT_FRM_DONE = 1; pj_bzero(tee->put_frm_flag, tee->dst_port_cnt * sizeof(tee->put_frm_flag[0])); for (i = 0; i < tee->dst_port_cnt; ++i) { pjmedia_frame frame_ = *frame; if (tee->put_frm_flag[i]) continue; if (tee->tee_conv[i].conv) { pj_status_t status; frame_.buf = tee->buf[0]; frame_.size = tee->tee_conv[i].conv_buf_size; status = pjmedia_converter_convert(tee->tee_conv[i].conv, frame, &frame_); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Failed to convert frame for destination" " port %d (%.*s)", i, tee->dst_ports[i].dst->info.name.slen, tee->dst_ports[i].dst->info.name.ptr)); continue; } } /* Find other destination ports which has the same format so * we don't need to do the same conversion twice. */ for (j = i; j < tee->dst_port_cnt; ++j) { pjmedia_frame framep; if (tee->put_frm_flag[j] || (tee->dst_ports[j].dst->info.fmt.id != tee->dst_ports[i].dst->info.fmt.id) || (tee->dst_ports[j].dst->info.fmt.det.vid.size.w != tee->dst_ports[i].dst->info.fmt.det.vid.size.w) || (tee->dst_ports[j].dst->info.fmt.det.vid.size.h != tee->dst_ports[i].dst->info.fmt.det.vid.size.h)) { continue; } framep = frame_; /* For dst_ports that do in-place processing, we need to duplicate * the data source first. */ if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC) { PJ_ASSERT_RETURN(tee->buf_size <= frame_.size, PJ_ETOOBIG); framep.buf = tee->buf[tee->buf_cnt-1]; framep.size = frame_.size; pj_memcpy(framep.buf, frame_.buf, frame_.size); } /* Deliver the data */ pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep); tee->put_frm_flag[j] = PUT_FRM_DONE; if (!tee->tee_conv[i].conv) break; } } return PJ_SUCCESS; }
static pj_status_t enc_dec_test(const char *codec_id, const char *filein, const char *fileout) { pj_pool_t *pool; pjmedia_codec_mgr *cm; pjmedia_codec *codec; const pjmedia_codec_info *pci; pjmedia_codec_param param; unsigned cnt, samples_per_frame; pj_str_t tmp; pjmedia_port *wavin, *wavout; unsigned lost_pct; pj_status_t status; #define T file_msec_duration/1000, file_msec_duration%1000 pool = pjmedia_endpt_create_pool(mept, "encdec", 1000, 1000); cm = pjmedia_endpt_get_codec_mgr(mept); #ifdef LOST_PCT lost_pct = LOST_PCT; #else lost_pct = 0; #endif cnt = 1; CHECK( pjmedia_codec_mgr_find_codecs_by_id(cm, pj_cstr(&tmp, codec_id), &cnt, &pci, NULL) ); CHECK( pjmedia_codec_mgr_get_default_param(cm, pci, ¶m) ); samples_per_frame = param.info.clock_rate * param.info.frm_ptime / 1000; /* Control VAD */ param.setting.vad = 1; /* Open wav for reading */ CHECK( pjmedia_wav_player_port_create(pool, filein, param.info.frm_ptime, PJMEDIA_FILE_NO_LOOP, 0, &wavin) ); /* Open wav for writing */ CHECK( pjmedia_wav_writer_port_create(pool, fileout, param.info.clock_rate, param.info.channel_cnt, samples_per_frame, 16, 0, 0, &wavout) ); /* Alloc codec */ CHECK( pjmedia_codec_mgr_alloc_codec(cm, pci, &codec) ); CHECK( codec->op->init(codec, pool) ); CHECK( codec->op->open(codec, ¶m) ); for (;;) { pjmedia_frame frm_pcm, frm_bit, out_frm, frames[4]; pj_int16_t pcmbuf[320]; pj_timestamp ts; pj_uint8_t bitstream[160]; frm_pcm.buf = (char*)pcmbuf; frm_pcm.size = samples_per_frame * 2; /* Read from WAV */ if (pjmedia_port_get_frame(wavin, &frm_pcm) != PJ_SUCCESS) break; if (frm_pcm.type != PJMEDIA_FRAME_TYPE_AUDIO) break;; /* Update duration */ file_msec_duration += samples_per_frame * 1000 / param.info.clock_rate; /* Encode */ frm_bit.buf = bitstream; frm_bit.size = sizeof(bitstream); CHECK(codec->op->encode(codec, &frm_pcm, sizeof(bitstream), &frm_bit)); /* On DTX, write zero frame to wavout to maintain duration */ if (frm_bit.size == 0 || frm_bit.type != PJMEDIA_FRAME_TYPE_AUDIO) { out_frm.buf = (char*)pcmbuf; out_frm.size = 160; CHECK( pjmedia_port_put_frame(wavout, &out_frm) ); TRACE_((THIS_FILE, "%d.%03d read: %u, enc: %u", T, frm_pcm.size, frm_bit.size)); continue; } /* Parse the bitstream (not really necessary for this case * since we always decode 1 frame, but it's still good * for testing) */ ts.u64 = 0; cnt = PJ_ARRAY_SIZE(frames); CHECK( codec->op->parse(codec, bitstream, frm_bit.size, &ts, &cnt, frames) ); CHECK( (cnt==1 ? PJ_SUCCESS : -1) ); /* Decode or simulate packet loss */ out_frm.buf = (char*)pcmbuf; out_frm.size = sizeof(pcmbuf); if ((pj_rand() % 100) < (int)lost_pct) { /* Simulate loss */ CHECK( codec->op->recover(codec, sizeof(pcmbuf), &out_frm) ); TRACE_((THIS_FILE, "%d.%03d Packet lost", T)); } else { /* Decode */ CHECK( codec->op->decode(codec, &frames[0], sizeof(pcmbuf), &out_frm) ); } /* Write to WAV */ CHECK( pjmedia_port_put_frame(wavout, &out_frm) ); TRACE_((THIS_FILE, "%d.%03d read: %u, enc: %u, dec/write: %u", T, frm_pcm.size, frm_bit.size, out_frm.size)); } /* Close wavs */ pjmedia_port_destroy(wavout); pjmedia_port_destroy(wavin); /* Close codec */ codec->op->close(codec); pjmedia_codec_mgr_dealloc_codec(cm, codec); /* Release pool */ pj_pool_release(pool); return PJ_SUCCESS; }