/* * Add a destination media port to the video tee. Create a converter if * necessary. */ pj_status_t pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee, unsigned option, pjmedia_port *port) { vid_tee_port *tee = (vid_tee_port*)vid_tee; pjmedia_video_format_detail *vfd; PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, PJ_EINVAL); if (tee->dst_port_cnt >= tee->dst_port_maxcnt) return PJ_ETOOMANY; pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0])); /* Check if we need to create a converter. */ vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE); if (vid_tee->info.fmt.id != port->info.fmt.id || vfd->size.w != vid_tee->info.fmt.det.vid.size.w || vfd->size.h != vid_tee->info.fmt.det.vid.size.h) { const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pjmedia_conversion_param conv_param; pj_status_t status; vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id); if (vfi == NULL) return PJMEDIA_EBADFMT; pj_bzero(&vafp, sizeof(vafp)); vafp.size = port->info.fmt.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) return status; realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? 2: 1, vafp.framebytes); pjmedia_format_copy(&conv_param.src, &vid_tee->info.fmt); pjmedia_format_copy(&conv_param.dst, &port->info.fmt); status = pjmedia_converter_create( NULL, tee->pool, &conv_param, &tee->tee_conv[tee->dst_port_cnt].conv); if (status != PJ_SUCCESS) return status; tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes; } else { realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? 1: 0, tee->buf_size); } tee->dst_ports[tee->dst_port_cnt].dst = port; tee->dst_ports[tee->dst_port_cnt].option = option; ++tee->dst_port_cnt; return PJ_SUCCESS; }
static pj_status_t create_converter(pjmedia_vid_port *vp) { if (vp->conv.conv) { pjmedia_converter_destroy(vp->conv.conv); vp->conv.conv = NULL; } /* Instantiate converter if necessary */ if (vp->conv.conv_param.src.id != vp->conv.conv_param.dst.id || (vp->conv.conv_param.src.det.vid.size.w != vp->conv.conv_param.dst.det.vid.size.w) || (vp->conv.conv_param.src.det.vid.size.h != vp->conv.conv_param.dst.det.vid.size.h)) { pj_status_t status; /* Yes, we need converter */ status = pjmedia_converter_create(NULL, vp->pool, &vp->conv.conv_param, &vp->conv.conv); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error creating converter")); return status; } } if (vp->conv.conv || (vp->role==ROLE_ACTIVE && (vp->dir & PJMEDIA_DIR_ENCODING))) { pj_status_t status; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; /* Allocate buffer for conversion */ vfi = pjmedia_get_video_format_info(NULL, vp->conv.conv_param.dst.id); if (!vfi) return PJMEDIA_EBADFMT; pj_bzero(&vafp, sizeof(vafp)); vafp.size = vp->conv.conv_param.dst.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) return PJMEDIA_EBADFMT; if (vafp.framebytes > vp->conv.conv_buf_size) { vp->conv.conv_buf = pj_pool_alloc(vp->pool, vafp.framebytes); vp->conv.conv_buf_size = vafp.framebytes; } } vp->conv.usec_ctr = 0; vp->conv.usec_src = PJMEDIA_PTIME(&vp->conv.conv_param.src.det.vid.fps); vp->conv.usec_dst = PJMEDIA_PTIME(&vp->conv.conv_param.dst.det.vid.fps); return PJ_SUCCESS; }
static pj_status_t create_converter(pjmedia_vid_port *vp) { /* Instantiate converter if necessary */ if (vp->conv_param.src.id != vp->conv_param.dst.id || vp->conv_param.src.det.vid.size.w != vp->conv_param.dst.det.vid.size.w || vp->conv_param.src.det.vid.size.h != vp->conv_param.dst.det.vid.size.h) { pj_status_t status; /* Yes, we need converter */ const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; if (vp->conv) { pjmedia_converter_destroy(vp->conv); vp->conv = NULL; } status = pjmedia_converter_create(NULL, vp->pool, &vp->conv_param, &vp->conv); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error creating converter")); return status; } /* Allocate buffer for conversion */ vfi = pjmedia_get_video_format_info(NULL, vp->conv_param.dst.id); if (!vfi) return PJMEDIA_EBADFMT; pj_bzero(&vafp, sizeof(vafp)); vafp.size = vp->conv_param.dst.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) return PJMEDIA_EBADFMT; if (vafp.framebytes > vp->conv_buf_size) { vp->conv_buf = pj_pool_alloc(vp->pool, vafp.framebytes); vp->conv_buf_size = vafp.framebytes; } } return PJ_SUCCESS; }
pj_status_t pjmedia_vid_dev_conv_create_converter(pjmedia_vid_dev_conv *conv, pj_pool_t *pool, pjmedia_format *fmt, pjmedia_rect_size src_size, pjmedia_rect_size dst_size, pj_bool_t handle_rotation, pj_bool_t maintain_aspect_ratio) { pj_status_t status; pjmedia_conversion_param conv_param; const pjmedia_video_format_info *vfi; pj_assert((src_size.w == dst_size.w || src_size.h == dst_size.h) || (src_size.w == dst_size.h || src_size.h == dst_size.w)); if (conv->conv) return PJ_SUCCESS; if (fmt->id != PJMEDIA_FORMAT_I420 && fmt->id != PJMEDIA_FORMAT_BGRA) return PJ_EINVAL; /* Currently, for BGRA format, device must handle the rotation. */ if (fmt->id == PJMEDIA_FORMAT_BGRA && handle_rotation) return PJ_ENOTSUP; if (handle_rotation) { #if !HAS_ROTATION return PJ_ENOTSUP; #endif } conv->src_size = src_size; conv->dst_size = dst_size; conv->handle_rotation = handle_rotation; pjmedia_format_copy(&conv->fmt, fmt); pjmedia_format_copy(&conv_param.src, fmt); pjmedia_format_copy(&conv_param.dst, fmt); /* If we do the rotation, the conversion's source size must be the same * as the device's original size. Otherwise, frames that require conversion * are the ones of which orientation differ by 90 or 270 degrees from the * destination size. */ if (handle_rotation) { conv_param.src.det.vid.size = src_size; } else { conv_param.src.det.vid.size.w = dst_size.h; conv_param.src.det.vid.size.h = dst_size.w; } /* Maintaining aspect ratio requires filling the left&right / * top&bottom area with black color. * Currently it is only supported for I420. * TODO: support BGRA as well */ if (fmt->id != PJMEDIA_FORMAT_I420) maintain_aspect_ratio = PJ_FALSE; /* Calculate the size after rotation. * If aspect ratio doesn't need to be maintained, rot_size is simply equal * to the destination size. Otherwise, we need to fit the rotated frame * to height or to width. */ conv->maintain_aspect_ratio = maintain_aspect_ratio; if (maintain_aspect_ratio) { conv->fit_to_h = (dst_size.w >= dst_size.h? PJ_TRUE: PJ_FALSE); if (conv->fit_to_h) { /* Fit to height */ conv->rot_size.h = dst_size.h; conv->rot_size.w = dst_size.h * dst_size.h / dst_size.w; /* Make sure the width difference is divisible by four * so we can have equal padding left and right. */ conv->rot_size.w += (dst_size.w - conv->rot_size.w) % 4; conv->pad = (conv->dst_size.w - conv->rot_size.w) / 2; } else { /* Fit to width */ conv->rot_size.w = dst_size.w; conv->rot_size.h = dst_size.w * dst_size.w / dst_size.h; conv->rot_size.h += (dst_size.h - conv->rot_size.h) % 4; conv->pad = (conv->dst_size.h - conv->rot_size.h) / 2; } } else { conv->rot_size = dst_size; } /* Calculate the size after resizing. */ if (handle_rotation) { /* If we do the rotation, conversion is done before rotation. */ if (maintain_aspect_ratio) { /* Since aspect ratio is maintained, the long side after * conversion must be the same as before conversion. * For example: 352x288 will be converted to 288x236 */ pj_size_t long_s = (conv->rot_size.h > conv->rot_size.w? conv->rot_size.h: conv->rot_size.w); pj_size_t short_s = (conv->rot_size.h > conv->rot_size.w? conv->rot_size.w: conv->rot_size.h); if (src_size.w > src_size.h) { conv->res_size.w = long_s; conv->res_size.h = short_s; } else { conv->res_size.w = short_s; conv->res_size.h = long_s; } } else { /* We don't need to maintain aspect ratio, * so just swap the width and height. * For example: 352x288 will be resized to 288x352 */ conv->res_size.w = src_size.h; conv->res_size.h = src_size.w; } conv_param.dst.det.vid.size = conv->res_size; } else { conv->res_size = conv->rot_size; conv_param.dst.det.vid.size = conv->rot_size; } status = pjmedia_converter_create(NULL, pool, &conv_param, &conv->conv); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Error creating converter")); return status; } vfi = pjmedia_get_video_format_info(NULL, fmt->id); pj_assert(vfi); conv->wxh = conv->dst_size.w * conv->dst_size.h; conv->src_frame_size = dst_size.w * dst_size.h * vfi->bpp / 8; conv->conv_frame_size = conv->rot_size.w * conv->rot_size.h; conv->conv_frame_size *= vfi->bpp / 8; conv->conv_buf = pj_pool_alloc(pool, conv->src_frame_size); pjmedia_vid_dev_conv_set_rotation(conv, PJMEDIA_ORIENT_NATURAL); PJ_LOG(4, (THIS_FILE, "Orientation converter created: %dx%d to %dx%d, " "maintain aspect ratio=%s", conv_param.src.det.vid.size.w, conv_param.src.det.vid.size.h, conv_param.dst.det.vid.size.w, conv_param.dst.det.vid.size.h, maintain_aspect_ratio? "yes": "no")); return PJ_SUCCESS; }
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; }