- Renamed vstreamutil.c to vid_steamutil.c just for filename format consistency reason.
 - Updated sample app simpleua.c and vid_streamutil.c to sync with updated API, e.g: strip session usage, two media ports exported video streams for each dir.
 - Added vid_streamutil.c capability to be able to stream video file (involving transcoding when video codec used in the file different to the video stream codec), also updated AVI player and ffmpeg codecs to be able to read and decode XVID/MPEG4 codec.
 - Fixed bug wrong media type check in stream.c and vid_stream.c in creating stream info from SDP.
 - Minor update: docs, logs, app samples makefiles.



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3425 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Nanang Izzuddin 2011-02-28 18:59:47 +00:00
parent a9c1cf45be
commit 235e1b4fa6
20 changed files with 2141 additions and 2033 deletions

View File

@ -980,10 +980,10 @@
/**
* Maximum video frame size.
* Default: 16kB
* Default: 128kB
*/
#ifndef PJMEDIA_MAX_VIDEO_FRAME_SIZE
# define PJMEDIA_MAX_VIDEO_FRAME_SIZE (1<<14)
#ifndef PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE
# define PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE (1<<17)
#endif

View File

@ -181,11 +181,13 @@ typedef enum pjmedia_format_id
PJMEDIA_FORMAT_H261 = PJMEDIA_FORMAT_PACK('H', '2', '6', '1'),
PJMEDIA_FORMAT_H263 = PJMEDIA_FORMAT_PACK('H', '2', '6', '3'),
PJMEDIA_FORMAT_H263P = PJMEDIA_FORMAT_PACK('P', '2', '6', '3'),
PJMEDIA_FORMAT_MJPEG = PJMEDIA_FORMAT_PACK('M', 'J', 'P', 'G'),
PJMEDIA_FORMAT_MPEG1VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '1', 'V'),
PJMEDIA_FORMAT_MPEG2VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '2', 'V'),
PJMEDIA_FORMAT_MPEG4 = PJMEDIA_FORMAT_PACK('M', 'P', 'G', '4'),
PJMEDIA_FORMAT_XVID = PJMEDIA_FORMAT_PACK('x', 'v', 'i', 'd'),
} pjmedia_format_id;

View File

@ -84,6 +84,18 @@ typedef struct pjmedia_vid_codec_param
} pjmedia_vid_codec_param;
/**
* Duplicate video codec parameter.
*
* @param pool The pool.
* @param src The video codec parameter to be duplicated.
*
* @return Duplicated codec parameter.
*/
PJ_DECL(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone(
pj_pool_t *pool,
const pjmedia_vid_codec_param *src);
/**
* Enumeration of video codec events.
*/
@ -516,6 +528,7 @@ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs(
pjmedia_vid_codec_info info[],
unsigned *prio);
/**
* Get codec info for the specified static payload type.
*
@ -531,6 +544,23 @@ pjmedia_vid_codec_mgr_get_codec_info( pjmedia_vid_codec_mgr *mgr,
unsigned pt,
const pjmedia_vid_codec_info **info);
/**
* Get codec info for the specified format ID.
*
* @param mgr The codec manager instance. If NULL, the default codec
* manager instance will be used.
* @param fmt_id Format ID. See #pjmedia_format_id
* @param info Pointer to receive codec info.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_vid_codec_mgr_get_codec_info2(pjmedia_vid_codec_mgr *mgr,
pjmedia_format_id fmt_id,
const pjmedia_vid_codec_info **info);
/**
* Convert codec info struct into a unique codec identifier.
* A codec identifier looks something like "H263/90000".

View File

@ -88,6 +88,7 @@ typedef struct pjmedia_vid_stream_info
sin_family is zero, the RTP address
will be calculated from RTP. */
unsigned tx_pt; /**< Outgoing codec paylaod type. */
unsigned rx_pt; /**< Incoming codec paylaod type. */
pj_uint32_t ssrc; /**< RTP SSRC. */
pj_uint32_t rtp_ts; /**< Initial RTP timestamp. */
pj_uint16_t rtp_seq; /**< Initial RTP sequence number. */
@ -219,18 +220,6 @@ PJ_DECL(pjmedia_transport*) pjmedia_vid_stream_get_transport(
pjmedia_vid_stream *st);
/**
* Start the video stream. This will start the appropriate channels
* in the video stream, depending on the video direction that was set
* when the stream was created.
*
* @param stream The video stream.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream);
/**
* Get the stream statistics. See also #pjmedia_stream_get_stat_jbuf()
*
@ -266,9 +255,30 @@ PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
pjmedia_jb_state *state);
PJ_DECL(pj_status_t) pjmedia_vid_stream_get_param(
/**
* Get the stream info.
*
* @param stream The video stream.
* @param info Video stream info.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_vid_stream_get_info(
const pjmedia_vid_stream *stream,
pjmedia_rtcp_stat *stat);
pjmedia_vid_stream_info *info);
/**
* Start the video stream. This will start the appropriate channels
* in the video stream, depending on the video direction that was set
* when the stream was created.
*
* @param stream The video stream.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream);
/**
* Pause the individual channel in the stream.

View File

@ -205,6 +205,7 @@ typedef pj_status_t (*func_parse_fmtp) (ffmpeg_private *ff);
struct ffmpeg_codec_desc {
/* Predefined info */
pjmedia_vid_codec_info info;
pjmedia_format_id base_fmt_id;
func_packetize packetize;
func_unpacketize unpacketize;
func_parse_fmtp parse_fmtp;
@ -245,15 +246,29 @@ static pj_status_t h263_parse_fmtp(ffmpeg_private *ff);
ffmpeg_codec_desc codec_desc[] =
{
{
{PJMEDIA_FORMAT_H263, {"H263",4}, PJMEDIA_RTP_PT_H263},
{PJMEDIA_FORMAT_H263P, {"H263-1998",9}, PJMEDIA_RTP_PT_H263},
PJMEDIA_FORMAT_H263,
&h263_packetize, &h263_unpacketize, &h263_parse_fmtp,
{2, { {{"CIF",3}, {"2",1}}, {{"QCIF",4}, {"1",1}}, } },
},
{
{PJMEDIA_FORMAT_H261, {"H261",4}, PJMEDIA_RTP_PT_H261},
{PJMEDIA_FORMAT_H263, {"H263",4}, PJMEDIA_RTP_PT_H263},
0,
&h263_packetize, &h263_unpacketize, &h263_parse_fmtp,
{2, { {{"CIF",3}, {"2",1}}, {{"QCIF",4}, {"1",1}}, } },
},
{
{PJMEDIA_FORMAT_MJPEG, {"JPEG",4}, PJMEDIA_RTP_PT_JPEG},
{PJMEDIA_FORMAT_H261, {"H261",4}, PJMEDIA_RTP_PT_H261},
},
{
{PJMEDIA_FORMAT_MJPEG, {"JPEG",4}, PJMEDIA_RTP_PT_JPEG},
},
{
{PJMEDIA_FORMAT_MPEG4, {"MP4V",4}, PJMEDIA_RTP_PT_MPV},
},
{
{PJMEDIA_FORMAT_XVID, {"XVID",4}, PJMEDIA_RTP_PT_MPV},
PJMEDIA_FORMAT_MPEG4,
},
};
@ -447,7 +462,7 @@ static pj_status_t h263_parse_fmtp(ffmpeg_private *ff)
static const ffmpeg_codec_desc* find_codec_info(
static const ffmpeg_codec_desc* find_codec_desc_by_info(
const pjmedia_vid_codec_info *info)
{
int i;
@ -468,7 +483,7 @@ static const ffmpeg_codec_desc* find_codec_info(
}
static int find_codec_info_idx_by_fmt_id(pjmedia_format_id fmt_id)
static int find_codec_idx_by_fmt_id(pjmedia_format_id fmt_id)
{
int i;
for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
@ -489,6 +504,7 @@ PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
pj_pool_t *pool;
AVCodec *c;
pj_status_t status;
unsigned i;
if (ffmpeg_factory.pool != NULL) {
/* Already initialized. */
@ -532,12 +548,13 @@ PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
* supported fps) are in the encoder.
*/
//PJ_LOG(3, (THIS_FILE, "%s", c->name));
status = CodecID_to_pjmedia_format_id(c->id, &fmt_id);
/* Skip if format ID is unknown */
if (status != PJ_SUCCESS)
continue;
codec_info_idx = find_codec_info_idx_by_fmt_id(fmt_id);
codec_info_idx = find_codec_idx_by_fmt_id(fmt_id);
/* Skip if codec is unwanted by this wrapper (not listed in
* the codec info array)
*/
@ -634,6 +651,59 @@ PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
desc->info.clock_rate = 90000;
}
/* Init unassigned encoder/decoder description from base codec */
for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
ffmpeg_codec_desc *desc = &codec_desc[i];
if (desc->base_fmt_id && (!desc->dec || !desc->enc)) {
ffmpeg_codec_desc *base_desc = NULL;
int base_desc_idx;
pjmedia_dir copied_dir = PJMEDIA_DIR_NONE;
base_desc_idx = find_codec_idx_by_fmt_id(desc->base_fmt_id);
if (base_desc_idx != -1)
base_desc = &codec_desc[base_desc_idx];
if (!base_desc || !base_desc->enabled)
continue;
/* Copy description from base codec */
if (!desc->info.dec_fmt_id_cnt) {
desc->info.dec_fmt_id_cnt = base_desc->info.dec_fmt_id_cnt;
pj_memcpy(desc->info.dec_fmt_id, base_desc->info.dec_fmt_id,
sizeof(pjmedia_format_id)*desc->info.dec_fmt_id_cnt);
}
if (!desc->info.fps_cnt) {
desc->info.fps_cnt = base_desc->info.fps_cnt;
pj_memcpy(desc->info.fps, base_desc->info.fps,
sizeof(desc->info.fps[0])*desc->info.fps_cnt);
}
if (!desc->info.clock_rate) {
desc->info.clock_rate = base_desc->info.clock_rate;
}
if (!desc->dec && base_desc->dec) {
copied_dir |= PJMEDIA_DIR_DECODING;
desc->dec = base_desc->dec;
}
if (!desc->enc && base_desc->enc) {
copied_dir |= PJMEDIA_DIR_ENCODING;
desc->enc = base_desc->enc;
}
desc->info.dir |= copied_dir;
desc->enabled = (desc->info.dir != PJMEDIA_DIR_NONE);
if (copied_dir != PJMEDIA_DIR_NONE) {
const char *dir_name[] = {NULL, "encoder", "decoder", "codec"};
PJ_LOG(5, (THIS_FILE, "The %.*s %s is using base codec (%.*s)",
desc->info.encoding_name.slen,
desc->info.encoding_name.ptr,
dir_name[copied_dir],
base_desc->info.encoding_name.slen,
base_desc->info.encoding_name.ptr));
}
}
}
/* Register codec factory to codec manager. */
status = pjmedia_vid_codec_mgr_register_factory(mgr,
&ffmpeg_factory.base);
@ -690,7 +760,7 @@ static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory,
PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
PJ_ASSERT_RETURN(info, PJ_EINVAL);
desc = find_codec_info(info);
desc = find_codec_desc_by_info(info);
if (!desc) {
return PJMEDIA_CODEC_EUNSUP;
}
@ -710,7 +780,7 @@ static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory,
PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
desc = find_codec_info(info);
desc = find_codec_desc_by_info(info);
if (!desc) {
return PJMEDIA_CODEC_EUNSUP;
}
@ -741,16 +811,20 @@ static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory,
unsigned *count,
pjmedia_vid_codec_info codecs[])
{
unsigned i;
unsigned i, max_cnt;
PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
*count = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc));
max_cnt = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc));
*count = 0;
for (i=0; i<*count; ++i) {
pj_memcpy(&codecs[i], &codec_desc[i].info,
sizeof(pjmedia_vid_codec_info));
for (i=0; i<max_cnt; ++i) {
if (codec_desc[i].enabled) {
pj_memcpy(&codecs[*count], &codec_desc[i].info,
sizeof(pjmedia_vid_codec_info));
(*count)++;
}
}
return PJ_SUCCESS;
@ -772,7 +846,7 @@ static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory,
PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
desc = find_codec_info(info);
desc = find_codec_desc_by_info(info);
if (!desc) {
return PJMEDIA_CODEC_EUNSUP;
}
@ -927,9 +1001,10 @@ static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff,
codec = ff->dec;
/* Decoding only attributes */
// this setting will be automatically fetched from the bitstream.
//ctx->coded_width = ff->param.dec_fmt.det.vid.size.w;
//ctx->coded_height = ff->param.dec_fmt.det.vid.size.h;
/* Width/height may be overriden by ffmpeg after first decoding. */
ctx->width = ctx->coded_width = ff->param.dec_fmt.det.vid.size.w;
ctx->height = ctx->coded_height = ff->param.dec_fmt.det.vid.size.h;
/* For decoder, be more flexible */
if (ff->param.dir!=PJMEDIA_DIR_ENCODING_DECODING ||
@ -1198,7 +1273,7 @@ static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP);
/* Validate output buffer size */
PJ_ASSERT_RETURN(ff->dec_vafp.framebytes <= output_buf_len, PJ_ETOOSMALL);
//PJ_ASSERT_RETURN(ff->dec_vafp.framebytes <= output_buf_len, PJ_ETOOSMALL);
/* Init frame to receive the decoded data, the ffmpeg codec context will
* automatically provide the decoded buffer (single buffer used for the
@ -1310,8 +1385,8 @@ static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
}
/* Check provided buffer size after format changed */
if (vafp->framebytes > output_buf_len)
return PJ_ETOOSMALL;
//if (vafp->framebytes > output_buf_len)
//return PJ_ETOOSMALL;
/* Get the decoded data */
for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) {

View File

@ -339,6 +339,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool,
/* Check supported video formats here */
if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES ||
(avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_MJPEG &&
avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_XVID &&
avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_UYVY &&
avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_YUY2 &&
avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_IYUV &&

View File

@ -51,12 +51,16 @@ static const struct ffmpeg_codec_table_t
enum CodecID codec_id;
} ffmpeg_codec_table[] =
{
{PJMEDIA_FORMAT_H261, CODEC_ID_H261},
{PJMEDIA_FORMAT_H263, CODEC_ID_H263},
{PJMEDIA_FORMAT_MPEG1VIDEO, CODEC_ID_MPEG1VIDEO},
{PJMEDIA_FORMAT_MPEG2VIDEO, CODEC_ID_MPEG2VIDEO},
{PJMEDIA_FORMAT_MPEG4, CODEC_ID_MPEG4},
{PJMEDIA_FORMAT_MJPEG, CODEC_ID_MJPEG},
{PJMEDIA_FORMAT_H261, CODEC_ID_H261},
{PJMEDIA_FORMAT_H263, CODEC_ID_H263},
{PJMEDIA_FORMAT_H263P, CODEC_ID_H263P},
{PJMEDIA_FORMAT_MPEG1VIDEO, CODEC_ID_MPEG1VIDEO},
{PJMEDIA_FORMAT_MPEG2VIDEO, CODEC_ID_MPEG2VIDEO},
{PJMEDIA_FORMAT_MPEG4, CODEC_ID_MPEG4},
{PJMEDIA_FORMAT_MJPEG, CODEC_ID_MJPEG},
#if LIBAVCODEC_VERSION_MAJOR < 53
{PJMEDIA_FORMAT_XVID, CODEC_ID_XVID},
#endif
};
static int pjmedia_ffmpeg_ref_cnt;

View File

@ -3034,7 +3034,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
return PJMEDIA_SDP_EMISSINGCONN;
/* Media type must be audio */
if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0)
if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0)
return PJMEDIA_EINVALIMEDIATYPE;
/* Get codec manager. */

View File

@ -67,6 +67,38 @@ struct pjmedia_vid_codec_mgr
static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
/*
* Duplicate video codec parameter.
*/
PJ_DEF(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone(
pj_pool_t *pool,
const pjmedia_vid_codec_param *src)
{
pjmedia_vid_codec_param *p;
unsigned i;
PJ_ASSERT_RETURN(pool && src, NULL);
p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param);
/* Update codec param */
pj_memcpy(p, src, sizeof(pjmedia_vid_codec_param));
for (i = 0; i < src->dec_fmtp.cnt; ++i) {
pj_strdup(pool, &p->dec_fmtp.param[i].name,
&src->dec_fmtp.param[i].name);
pj_strdup(pool, &p->dec_fmtp.param[i].val,
&src->dec_fmtp.param[i].val);
}
for (i = 0; i < src->enc_fmtp.cnt; ++i) {
pj_strdup(pool, &p->enc_fmtp.param[i].name,
&src->enc_fmtp.param[i].name);
pj_strdup(pool, &p->enc_fmtp.param[i].val,
&src->enc_fmtp.param[i].val);
}
return p;
}
/*
* Initialize codec manager.
*/
@ -305,6 +337,35 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info(
}
PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info2(
pjmedia_vid_codec_mgr *mgr,
pjmedia_format_id fmt_id,
const pjmedia_vid_codec_info **p_info)
{
unsigned i;
PJ_ASSERT_RETURN(p_info, PJ_EINVAL);
if (!mgr) mgr = def_vid_codec_mgr;
PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
pj_mutex_lock(mgr->mutex);
for (i=0; i<mgr->codec_cnt; ++i) {
if (mgr->codec_desc[i].info.fmt_id == fmt_id) {
*p_info = &mgr->codec_desc[i].info;
pj_mutex_unlock(mgr->mutex);
return PJ_SUCCESS;
}
}
pj_mutex_unlock(mgr->mutex);
return PJMEDIA_CODEC_EUNSUP;
}
/*
* Convert codec info struct into a unique codec identifier.
* A codec identifier looks something like "L16/44100/2".
@ -632,23 +693,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_default_param(
return PJ_SUCCESS;
}
p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param);
p = codec_desc->def_param;
/* Update codec param */
pj_memcpy(p, param, sizeof(pjmedia_vid_codec_param));
for (i = 0; i < param->dec_fmtp.cnt; ++i) {
pj_strdup(pool, &p->dec_fmtp.param[i].name,
&param->dec_fmtp.param[i].name);
pj_strdup(pool, &p->dec_fmtp.param[i].val,
&param->dec_fmtp.param[i].val);
}
for (i = 0; i < param->enc_fmtp.cnt; ++i) {
pj_strdup(pool, &p->enc_fmtp.param[i].name,
&param->enc_fmtp.param[i].name);
pj_strdup(pool, &p->enc_fmtp.param[i].val,
&param->enc_fmtp.param[i].val);
}
/* Update codec default param */
p = pjmedia_vid_codec_param_clone(pool, param);
if (!p)
return PJ_EINVAL;
codec_desc->def_param = p;
pj_mutex_unlock(mgr->mutex);

View File

@ -84,6 +84,7 @@ struct pjmedia_vid_stream
{
pjmedia_endpt *endpt; /**< Media endpoint. */
pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */
pjmedia_vid_stream_info info; /**< Stream info. */
pjmedia_vid_channel *enc; /**< Encoding channel. */
pjmedia_vid_channel *dec; /**< Decoding channel. */
@ -128,15 +129,11 @@ struct pjmedia_vid_stream
#endif
#if TRACE_JB
pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/
char *trace_jb_buf; /**< Jitter tracing buffer. */
pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/
char *trace_jb_buf; /**< Jitter tracing buffer. */
#endif
pjmedia_vid_codec *codec; /**< Codec instance being used. */
pjmedia_vid_codec_info codec_info; /**< Codec param. */
pjmedia_vid_codec_param codec_param; /**< Codec param. */
pjmedia_vid_stream_info info;
pjmedia_vid_codec *codec; /**< Codec instance being used. */
};
@ -626,11 +623,7 @@ static void on_rx_rtp( void *data,
if (seq_st.status.flag.restart) {
status = pjmedia_jbuf_reset(stream->jb);
PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset"));
} else {
/* Video stream */
/* Just put the payload into jitter buffer */
pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0,
pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL);
@ -927,10 +920,11 @@ static pj_status_t get_frame(pjmedia_port *port,
/* Check if the decoder format is changed */
if (frame->bit_info & PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED) {
/* Update param from codec */
stream->codec->op->get_param(stream->codec, &stream->codec_param);
stream->codec->op->get_param(stream->codec, stream->info.codec_param);
/* Update decoding channel port info */
stream->dec->port.info.fmt = stream->codec_param.dec_fmt;
pjmedia_format_copy(&stream->dec->port.info.fmt,
&stream->info.codec_param->dec_fmt);
}
return PJ_SUCCESS;
@ -964,10 +958,10 @@ static pj_status_t create_channel( pj_pool_t *pool,
/* Init vars */
if (dir==PJMEDIA_DIR_DECODING) {
type_name = "vstrmdec";
type_name = "vstdec";
fmt = &info->codec_param->dec_fmt;
} else {
type_name = "vstrmenc";
type_name = "vstenc";
fmt = &info->codec_param->enc_fmt;
}
@ -1053,6 +1047,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream);
PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
/* Copy stream info */
pj_memcpy(&stream->info, info, sizeof(*info));
/* Get codec manager */
stream->codec_mgr = pjmedia_vid_codec_mgr_instance();
PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED);
@ -1063,7 +1060,6 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
"vstrm%p", stream);
/* Create and initialize codec: */
stream->codec_info = info->codec_info;
status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr,
&info->codec_info,
&stream->codec);
@ -1072,18 +1068,27 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
/* Get codec param: */
if (info->codec_param)
stream->codec_param = *info->codec_param;
else {
if (info->codec_param) {
stream->info.codec_param = pjmedia_vid_codec_param_clone(
pool,
info->codec_param);
} else {
pjmedia_vid_codec_param def_param;
status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr,
&info->codec_info,
&stream->codec_param);
&def_param);
if (status != PJ_SUCCESS)
return status;
stream->info.codec_param = pjmedia_vid_codec_param_clone(
pool,
&def_param);
pj_assert(stream->info.codec_param);
}
vfd_enc = pjmedia_format_get_video_format_detail(
&stream->codec_param.enc_fmt, 1);
&stream->info.codec_param->enc_fmt,
PJ_TRUE);
/* Init stream: */
stream->endpt = endpt;
@ -1116,14 +1121,15 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
return status;
/* Init codec param */
stream->codec_param.dir = info->dir;
stream->codec_param.enc_mtu = PJMEDIA_MAX_MTU - sizeof(pjmedia_rtp_hdr);
stream->info.codec_param->dir = info->dir;
stream->info.codec_param->enc_mtu = PJMEDIA_MAX_MTU -
sizeof(pjmedia_rtp_hdr);
/* Init and open the codec. */
status = stream->codec->op->init(stream->codec, pool);
if (status != PJ_SUCCESS)
return status;
status = stream->codec->op->open(stream->codec, &stream->codec_param);
status = stream->codec->op->open(stream->codec, stream->info.codec_param);
if (status != PJ_SUCCESS)
return status;
@ -1139,9 +1145,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
/* Validate the frame size */
if (stream->frame_size == 0 ||
stream->frame_size > PJMEDIA_MAX_VIDEO_FRAME_SIZE)
stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE)
{
stream->frame_size = PJMEDIA_MAX_VIDEO_FRAME_SIZE;
stream->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE;
}
/* Get frame length in timestamp unit */
@ -1150,7 +1156,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
/* Create decoder channel */
status = create_channel( pool, stream, PJMEDIA_DIR_DECODING,
info->codec_info.pt, info, &stream->dec);
info->rx_pt, info, &stream->dec);
if (status != PJ_SUCCESS)
return status;
@ -1406,34 +1412,6 @@ PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport(
}
/*
* Start stream.
*/
PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream)
{
PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);
if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {
stream->enc->paused = 0;
//pjmedia_snd_stream_start(stream->enc->snd_stream);
PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started"));
} else {
PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
}
if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {
stream->dec->paused = 0;
//pjmedia_snd_stream_start(stream->dec->snd_stream);
PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started"));
} else {
PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
}
return PJ_SUCCESS;
}
/*
* Get stream statistics.
*/
@ -1491,6 +1469,48 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
return pjmedia_jbuf_get_state(stream->jb, state);
}
/*
* Get the stream info.
*/
PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info(
const pjmedia_vid_stream *stream,
pjmedia_vid_stream_info *info)
{
PJ_ASSERT_RETURN(stream && info, PJ_EINVAL);
pj_memcpy(info, &stream->info, sizeof(*info));
return PJ_SUCCESS;
}
/*
* Start stream.
*/
PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream)
{
PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);
if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {
stream->enc->paused = 0;
//pjmedia_snd_stream_start(stream->enc->snd_stream);
PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started"));
} else {
PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
}
if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {
stream->dec->paused = 0;
//pjmedia_snd_stream_start(stream->dec->snd_stream);
PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started"));
} else {
PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
}
return PJ_SUCCESS;
}
/*
* Pause stream.
*/
@ -1578,6 +1598,7 @@ static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
* For dynamic payload types, MUST get the rtpmap.
*/
pt = pj_strtoul(&local_m->desc.fmt[0]);
si->rx_pt = pt;
if (pt < 96) {
const pjmedia_vid_codec_info *p_info;
@ -1659,7 +1680,7 @@ static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
&si->codec_param->enc_fmtp);
/* Get local fmtp for our decoder. */
pjmedia_stream_info_parse_fmtp(pool, local_m, pt,
pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt,
&si->codec_param->dec_fmtp);
/* When direction is NONE (it means SDP negotiation has failed) we don't
@ -1717,7 +1738,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
return PJMEDIA_SDP_EMISSINGCONN;
/* Media type must be audio */
if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0)
if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0)
return PJMEDIA_EINVALIMEDIATYPE;

View File

@ -20,18 +20,21 @@ static pj_status_t codec_put_frame(pjmedia_port *port,
pjmedia_frame *frame)
{
codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
pjmedia_vid_codec *codec = port_data->codec;
pjmedia_frame enc_frame;
pj_status_t status;
enc_frame.buf = port_data->enc_buf;
enc_frame.size = port_data->enc_buf_size;
#if !BYPASS_CODEC
status = codec->op->encode(codec, frame, enc_frame.size, &enc_frame);
if (status != PJ_SUCCESS) goto on_error;
status = codec->op->decode(codec, &enc_frame, frame->size, frame);
if (status != PJ_SUCCESS) goto on_error;
{
pjmedia_vid_codec *codec = port_data->codec;
pjmedia_frame enc_frame;
enc_frame.buf = port_data->enc_buf;
enc_frame.size = port_data->enc_buf_size;
status = codec->op->encode(codec, frame, enc_frame.size, &enc_frame);
if (status != PJ_SUCCESS) goto on_error;
status = codec->op->decode(codec, &enc_frame, frame->size, frame);
if (status != PJ_SUCCESS) goto on_error;
}
#endif
status = pjmedia_port_put_frame(port_data->dn_port, frame);
@ -87,6 +90,7 @@ static int enum_codecs()
static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
pjmedia_format_id raw_fmt_id)
{
const pj_str_t port_name = {"codec", 5};
pjmedia_vid_codec *codec=NULL;
pjmedia_port codec_port;
@ -162,6 +166,50 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
}
}
/* Prepare codec */
{
pj_str_t codec_id_st;
unsigned info_cnt = 1;
const pjmedia_vid_codec_info *codec_info;
/* Lookup codec */
pj_cstr(&codec_id_st, 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;
}
#if !BYPASS_CODEC
/* Open codec */
status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
&codec);
if (status != PJ_SUCCESS) {
rc = 250; goto on_return;
}
status = codec->op->init(codec, pool);
if (status != PJ_SUCCESS) {
rc = 251; goto on_return;
}
codec_param.dec_fmt.id = raw_fmt_id;
status = codec->op->open(codec, &codec_param);
if (status != PJ_SUCCESS) {
rc = 252; goto on_return;
}
#endif /* !BYPASS_CODEC */
}
pjmedia_vid_port_param_default(&vport_param);
/* Create capture, set it to active (master) */
@ -170,6 +218,7 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
if (status != PJ_SUCCESS) {
rc = 220; goto on_return;
}
pjmedia_format_copy(&vport_param.vidparam.fmt, &codec_param.dec_fmt);
vport_param.vidparam.fmt.id = raw_fmt_id;
vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
vport_param.active = PJ_TRUE;
@ -200,76 +249,24 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
rc = 230; goto on_return;
}
/* Prepare codec */
{
pj_str_t codec_id_st;
unsigned info_cnt = 1;
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;
/* Lookup codec */
pj_cstr(&codec_id_st, 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.dec_fmt, &vport_param.vidparam.fmt);
#if !BYPASS_CODEC
/* Open codec */
status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
&codec);
if (status != PJ_SUCCESS) {
rc = 250; goto on_return;
}
status = codec->op->init(codec, pool);
if (status != PJ_SUCCESS) {
rc = 251; goto on_return;
}
status = codec->op->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);
#endif /* !BYPASS_CODEC */
/* 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;
}
codec_port_data.codec = codec;
codec_port_data.dn_port = pjmedia_vid_port_get_passive_port(renderer);
codec_port_data.enc_buf = enc_buf;
codec_port_data.enc_buf_size = enc_buf_size;
codec_port.put_frame = &codec_put_frame;
codec_port.port_data.pdata = &codec_port_data;
/* 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;
}
codec_port_data.codec = codec;
codec_port_data.dn_port = pjmedia_vid_port_get_passive_port(renderer);
codec_port_data.enc_buf_size = codec_param.dec_fmt.det.vid.size.w *
codec_param.dec_fmt.det.vid.size.h * 4;
codec_port_data.enc_buf = pj_pool_alloc(pool,
codec_port_data.enc_buf_size);
codec_port.put_frame = &codec_put_frame;
codec_port.port_data.pdata = &codec_port_data;
/* Connect capture to codec port */
status = pjmedia_vid_port_connect(capture,
@ -279,15 +276,27 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
rc = 270; goto on_return;
}
PJ_LOG(3, (THIS_FILE, " starting codec test: %c%c%c%c<->%s %dx%d",
#if BYPASS_CODEC
PJ_LOG(3, (THIS_FILE, " starting loopback test: %c%c%c%c %dx%d",
((raw_fmt_id & 0x000000FF) >> 0),
((raw_fmt_id & 0x0000FF00) >> 8),
((raw_fmt_id & 0x00FF0000) >> 16),
((raw_fmt_id & 0xFF000000) >> 24),
codec_param.dec_fmt.det.vid.size.w,
codec_param.dec_fmt.det.vid.size.h
));
#else
PJ_LOG(3, (THIS_FILE, " starting codec test: %c%c%c%c<->%.*s %dx%d",
((codec_param.dec_fmt.id & 0x000000FF) >> 0),
((codec_param.dec_fmt.id & 0x0000FF00) >> 8),
((codec_param.dec_fmt.id & 0x00FF0000) >> 16),
((codec_param.dec_fmt.id & 0xFF000000) >> 24),
codec_id,
codec_info->encoding_name.slen,
codec_info->encoding_name.ptr,
codec_param.dec_fmt.det.vid.size.w,
codec_param.dec_fmt.det.vid.size.h
));
#endif
/* Start streaming.. */
status = pjmedia_vid_port_start(renderer);
@ -327,6 +336,10 @@ int vid_codec_test(void)
pj_pool_t *pool;
int rc = 0;
pj_status_t status;
int orig_log_level;
orig_log_level = pj_log_get_level();
pj_log_set_level(6);
PJ_LOG(3, (THIS_FILE, "Performing video codec tests.."));
@ -344,7 +357,7 @@ int vid_codec_test(void)
if (rc != 0)
goto on_return;
rc = encode_decode_test(pool, "mjpeg", 0);
rc = encode_decode_test(pool, "h263", 0);
if (rc != 0)
goto on_return;
@ -352,6 +365,7 @@ on_return:
pjmedia_codec_ffmpeg_deinit();
pjmedia_vid_subsys_shutdown();
pj_pool_release(pool);
pj_log_set_level(orig_log_level);
return rc;
}

View File

@ -57,7 +57,7 @@ static int enum_devs(void)
return PJ_SUCCESS;
}
static pj_status_t vid_event_cb(pjmedia_vid_stream *stream,
static pj_status_t vid_event_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_vid_event *event)
{

View File

@ -108,25 +108,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua_wince", "pjsip-apps\s
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpjproject", "pjsip-apps\build\libpjproject.vcproj", "{23D7679C-764C-4E02-8B29-BB882CEEEFE2}"
ProjectSection(ProjectDependencies) = postProject
{F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}
{A1989FF3-9894-40F4-B5A6-6EA364476E45} = {A1989FF3-9894-40F4-B5A6-6EA364476E45}
{E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D}
{9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}
{B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D}
{DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8}
{B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21}
{4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}
{855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}
{4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9}
{3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA}
{7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}
{6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E}
{FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}
{4281CA5E-1D48-45D4-A991-2718A454B4BA} = {4281CA5E-1D48-45D4-A991-2718A454B4BA}
{A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}
{4BF51C21-5A30-423B-82FE-1ED410E5769D} = {4BF51C21-5A30-423B-82FE-1ED410E5769D}
{2A3F241E-682C-47E1-9543-DC28708B406A} = {2A3F241E-682C-47E1-9543-DC28708B406A}
{2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031}
{2A3F241E-682C-47E1-9543-DC28708B406A} = {2A3F241E-682C-47E1-9543-DC28708B406A}
{4BF51C21-5A30-423B-82FE-1ED410E5769D} = {4BF51C21-5A30-423B-82FE-1ED410E5769D}
{A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}
{4281CA5E-1D48-45D4-A991-2718A454B4BA} = {4281CA5E-1D48-45D4-A991-2718A454B4BA}
{FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}
{6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E}
{7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}
{3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA}
{4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9}
{855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}
{4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}
{B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21}
{DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8}
{B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D}
{E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D}
{A1989FF3-9894-40F4-B5A6-6EA364476E45} = {A1989FF3-9894-40F4-B5A6-6EA364476E45}
{F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_pjsua", "pjsip-apps\build\python_pjsua.vcproj", "{0C91838B-3372-40B4-A764-DE075A4BC94B}"

View File

@ -88,7 +88,7 @@ SAMPLES = $(BINDIR)\auddemo.exe \
$(BINDIR)\streamutil.exe \
$(BINDIR)\strerror.exe \
$(BINDIR)\tonegen.exe \
$(BINDIR)\vstreamutil.exe
$(BINDIR)\vid_streamutil.exe
all: $(BINDIR) $(OBJDIR) $(SAMPLES)

View File

@ -40,7 +40,7 @@ SAMPLES := auddemo \
streamutil \
strerror \
tonegen \
vstreamutil
vid_streamutil
EXES := $(foreach file, $(SAMPLES), $(BINDIR)/$(file)$(HOST_EXE))

File diff suppressed because it is too large Load Diff

View File

@ -85,6 +85,7 @@ struct codec_fmt {
PJ_TRUE , PJMEDIA_FORMAT_I420},
{PJMEDIA_FORMAT_H263 , "h263" ,
PJ_FALSE, 0},
{PJMEDIA_FORMAT_XVID , "xvid"},
};
typedef struct avi_port_t
@ -105,7 +106,7 @@ typedef struct codec_port_data_t
pjmedia_converter *conv;
} codec_port_data_t;
static pj_status_t avi_event_cb(pjmedia_vid_stream *stream,
static pj_status_t avi_event_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_vid_event *event)
{

View File

@ -67,11 +67,19 @@
/* Settings */
#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6.
#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6.
* PJ_HAS_IPV6 must be enabled and
* your system must support IPv6. */
#define SIP_PORT 5060 /* Listening SIP port */
#define RTP_PORT 4000 /* RTP port */
#if 0
#define SIP_PORT 5080 /* Listening SIP port */
#define RTP_PORT 5000 /* RTP port */
#else
#define SIP_PORT 5060 /* Listening SIP port */
#define RTP_PORT 4000 /* RTP port */
#endif
#define MAX_MEDIA_CNT 2 /* Media count, set to 1 for audio
* only or 2 for audio and video */
/*
* Static variables.
@ -82,14 +90,22 @@ static pjsip_endpoint *g_endpt; /* SIP endpoint. */
static pj_caching_pool cp; /* Global pool factory. */
static pjmedia_endpt *g_med_endpt; /* Media endpoint. */
static pjmedia_transport_info g_med_tpinfo; /* Socket info for media */
static pjmedia_transport *g_med_transport;/* Media stream transport */
static pjmedia_transport_info g_med_tpinfo[MAX_MEDIA_CNT];
/* Socket info for media */
static pjmedia_transport *g_med_transport[MAX_MEDIA_CNT];
/* Media stream transport */
static pjmedia_sock_info g_sock_info[MAX_MEDIA_CNT];
/* Socket info array */
/* Call variables: */
static pjsip_inv_session *g_inv; /* Current invite session. */
static pjmedia_session *g_med_session; /* Call's media session. */
static pjmedia_stream *g_med_stream; /* Call's audio stream. */
static pjmedia_vid_stream *g_med_vstream; /* Call's video stream. */
static pjmedia_snd_port *g_snd_player; /* Call's sound player */
static pjmedia_snd_port *g_snd_rec; /* Call's sound recorder. */
static pjmedia_vid_port *g_vid_capturer;/* Call's video capturer. */
static pjmedia_vid_port *g_vid_renderer;/* Call's video renderer. */
/*
@ -136,6 +152,68 @@ static pjsip_module mod_simpleua =
};
/* Notification on incoming messages */
static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
{
PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
"%.*s\n"
"--end msg--",
rdata->msg_info.len,
pjsip_rx_data_get_info(rdata),
rdata->tp_info.transport->type_name,
rdata->pkt_info.src_name,
rdata->pkt_info.src_port,
(int)rdata->msg_info.len,
rdata->msg_info.msg_buf));
/* Always return false, otherwise messages will not get processed! */
return PJ_FALSE;
}
/* Notification on outgoing messages */
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
{
/* Important note:
* tp_info field is only valid after outgoing messages has passed
* transport layer. So don't try to access tp_info when the module
* has lower priority than transport layer.
*/
PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
"%.*s\n"
"--end msg--",
(tdata->buf.cur - tdata->buf.start),
pjsip_tx_data_get_info(tdata),
tdata->tp_info.transport->type_name,
tdata->tp_info.dst_name,
tdata->tp_info.dst_port,
(int)(tdata->buf.cur - tdata->buf.start),
tdata->buf.start));
/* Always return success, otherwise message will not get sent! */
return PJ_SUCCESS;
}
/* The module instance. */
static pjsip_module msg_logger =
{
NULL, NULL, /* prev, next. */
{ "mod-msg-log", 13 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&logging_on_rx_msg, /* on_rx_request() */
&logging_on_rx_msg, /* on_rx_response() */
&logging_on_tx_msg, /* on_tx_request. */
&logging_on_tx_msg, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
/*
* main()
@ -145,12 +223,15 @@ static pjsip_module mod_simpleua =
*/
int main(int argc, char *argv[])
{
pj_pool_t *pool;
pj_status_t status;
unsigned i;
/* Must init PJLIB first: */
status = pj_init();
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
pj_log_set_level(5);
/* Then init PJLIB-UTIL: */
status = pjlib_util_init();
@ -262,6 +343,12 @@ int main(int argc, char *argv[])
status = pjsip_endpt_register_module( g_endpt, &mod_simpleua);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
/*
* Register message logger module.
*/
status = pjsip_endpt_register_module( g_endpt, &msg_logger);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
/*
* Initialize media endpoint.
@ -284,28 +371,49 @@ int main(int argc, char *argv[])
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
#endif
/* Init video subsystem */
pool = pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512);
status = pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjmedia_converter_mgr_create(pool, NULL);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjmedia_vid_codec_mgr_create(pool, NULL);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjmedia_vid_subsys_init(&cp.factory);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
/* Init ffmpeg video codecs */
status = pjmedia_codec_ffmpeg_init(NULL, &cp.factory);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
/*
* Create media transport used to send/receive RTP/RTCP socket.
* One media transport is needed for each call. Application may
* opt to re-use the same media transport for subsequent calls.
*/
status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL,
RTP_PORT, 0, &g_med_transport);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create media transport", status);
return 1;
for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) {
status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL,
RTP_PORT + i*2, 0,
&g_med_transport[i]);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create media transport", status);
return 1;
}
/*
* Get socket info (address, port) of the media transport. We will
* need this info to create SDP (i.e. the address and port info in
* the SDP).
*/
pjmedia_transport_info_init(&g_med_tpinfo[i]);
pjmedia_transport_get_info(g_med_transport[i], &g_med_tpinfo[i]);
pj_memcpy(&g_sock_info[i], &g_med_tpinfo[i].sock_info,
sizeof(pjmedia_sock_info));
}
/*
* Get socket info (address, port) of the media transport. We will
* need this info to create SDP (i.e. the address and port info in
* the SDP).
*/
pjmedia_transport_info_init(&g_med_tpinfo);
pjmedia_transport_get_info(g_med_transport, &g_med_tpinfo);
/*
* If URL is specified, then make call immediately.
*/
@ -366,9 +474,8 @@ int main(int argc, char *argv[])
*/
status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */
dlg->pool, /* pool. */
1, /* # of streams */
&g_med_tpinfo.sock_info,
/* RTP sock info */
MAX_MEDIA_CNT, /* # of streams */
g_sock_info, /* RTP sock info */
&local_sdp); /* the SDP result */
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
@ -440,6 +547,8 @@ int main(int argc, char *argv[])
/* On exit, dump current memory usage: */
dump_pool_usage(THIS_FILE, &cp);
pj_pool_release(pool);
return 0;
}
@ -574,9 +683,8 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
* Get media capability from media endpoint:
*/
status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, 1,
&g_med_tpinfo.sock_info,
&local_sdp);
status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool,
MAX_MEDIA_CNT, g_sock_info, &local_sdp);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
@ -639,7 +747,7 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
static void call_on_media_update( pjsip_inv_session *inv,
pj_status_t status)
{
pjmedia_session_info sess_info;
pjmedia_stream_info stream_info;
const pjmedia_sdp_session *local_sdp;
const pjmedia_sdp_session *remote_sdp;
pjmedia_port *media_port;
@ -662,41 +770,44 @@ static void call_on_media_update( pjsip_inv_session *inv,
status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
/* Create session info based on the two SDPs.
* We only support one stream per session for now.
*/
status = pjmedia_session_info_from_sdp(inv->dlg->pool, g_med_endpt,
1, &sess_info,
local_sdp, 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 media session", status);
app_perror(THIS_FILE,"Unable to create audio stream info",status);
return;
}
/* If required, we can also change some settings in the session info,
/* If required, we can also change some settings in the stream info,
* (such as jitter buffer settings, codec settings, etc) before we
* create the session.
* create the stream.
*/
/* Create new media session, passing the two SDPs, and also the
/* Create new audio media stream, passing the stream info, and also the
* media socket that we created earlier.
* The media session is active immediately.
*/
status = pjmedia_session_create( g_med_endpt, &sess_info,
&g_med_transport, NULL, &g_med_session );
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 media session", status);
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 first stream in the session.
/* 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_session_get_port(g_med_session, 0, &media_port);
pjmedia_stream_get_port(g_med_stream, &media_port);
@ -745,6 +856,149 @@ static void call_on_media_update( pjsip_inv_session *inv,
status = pjmedia_snd_port_connect(g_snd_rec, 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 (local_sdp->media_count > 1) {
pjmedia_vid_stream_info vstream_info;
pjmedia_vid_port_param 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, inv->dlg->pool,
&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 renderer format from decoding format in stream info */
pjmedia_format_copy(&vport_param.vidparam.fmt,
&vstream_info.codec_param->dec_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;
}
/* Get video stream port for decoding direction */
pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_DECODING,
&media_port);
/* 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 renderer capturer from encoding format in stream info */
pjmedia_format_copy(&vport_param.vidparam.fmt,
&vstream_info.codec_param->enc_fmt);
/* Capturer format ID should be copied from decoder format ID? */
vport_param.vidparam.fmt.id = vstream_info.codec_param->dec_fmt.id;
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;
}
/* Get video stream port for decoding direction */
pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_ENCODING,
&media_port);
/* 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;
}
}
}
/* Done with media. */
}

View File

@ -0,0 +1,907 @@
/* $Id$ */
/*
* Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* \page page_pjmedia_samples_vid_streamutil_c Samples: Video Streaming
*
* This example mainly demonstrates how to stream video to remote
* peer using RTP.
*
* This file is pjsip-apps/src/samples/vid_streamutil.c
*
* \includelineno vid_streamutil.c
*/
#include <pjlib.h>
#include <pjlib-util.h>
#include <pjmedia.h>
#include <pjmedia-codec.h>
#include <pjmedia/transport_srtp.h>
#include <stdlib.h> /* atoi() */
#include <stdio.h>
#include "util.h"
static const char *desc =
" vid_streamutil \n"
"\n"
" PURPOSE: \n"
" Demonstrate how to use pjmedia video stream component to \n"
" transmit/receive RTP packets to/from video device/file. \n"
"\n"
"\n"
" USAGE: \n"
" vid_streamutil [options] \n"
"\n"
"\n"
" Options: \n"
" --codec=CODEC Set the codec name. \n"
" --local-port=PORT Set local RTP port (default=4000) \n"
" --remote=IP:PORT Set the remote peer. If this option is set, \n"
" the program will transmit RTP audio to the \n"
" specified address. (default: recv only) \n"
" --play-file=AVI Send video from the AVI file instead of from \n"
" the video device. \n"
" --send-recv Set stream direction to bidirectional. \n"
" --send-only Set stream direction to send only \n"
" --recv-only Set stream direction to recv only (default) \n"
" --send-width Video width to be sent \n"
" --send-height Video height to be sent \n"
" --send-width and --send-height not applicable \n"
" for file streaming (see --play-file) \n"
" --send-pt Payload type for sending \n"
" --recv-pt Payload type for receiving \n"
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
" --use-srtp[=NAME] Enable SRTP with crypto suite NAME \n"
" e.g: AES_CM_128_HMAC_SHA1_80 (default), \n"
" AES_CM_128_HMAC_SHA1_32 \n"
" Use this option along with the TX & RX keys, \n"
" formated of 60 hex digits (e.g: E148DA..) \n"
" --srtp-tx-key SRTP key for transmiting \n"
" --srtp-rx-key SRTP key for receiving \n"
#endif
"\n"
;
#define THIS_FILE "vid_streamutil.c"
#define HAS_LOCAL_RENDERER_FOR_PLAY_FILE 1
#define DEF_RENDERER_WIDTH 0
#define DEF_RENDERER_HEIGHT 0
/* Prototype */
static void print_stream_stat(pjmedia_vid_stream *stream,
const pjmedia_vid_codec_param *codec_param);
/* Prototype for LIBSRTP utility in file datatypes.c */
int hex_string_to_octet_string(char *raw, char *hex, int len);
/*
* Register all codecs.
*/
static pj_status_t init_codecs(pj_pool_factory *pf)
{
pj_status_t status;
/* To suppress warning about unused var when all codecs are disabled */
PJ_UNUSED_ARG(status);
#if defined(PJMEDIA_HAS_FFMPEG_CODEC) && PJMEDIA_HAS_FFMPEG_CODEC != 0
status = pjmedia_codec_ffmpeg_init(NULL, pf);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
#endif
return PJ_SUCCESS;
}
static pj_status_t create_file_player( pj_pool_t *pool,
const char *file_name,
pjmedia_port **p_play_port)
{
pjmedia_avi_streams *avi_streams;
pjmedia_avi_stream *vid_stream;
pjmedia_port *play_port;
pj_status_t status;
status = pjmedia_avi_player_create_streams(pool, file_name, 0, &avi_streams);
if (status != PJ_SUCCESS)
return status;
vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
0,
PJMEDIA_TYPE_VIDEO);
if (!vid_stream)
return PJ_ENOTFOUND;
play_port = pjmedia_avi_stream_get_port(vid_stream);
pj_assert(play_port);
*p_play_port = play_port;
return PJ_SUCCESS;
}
/*
* Create stream based on the codec, dir, remote address, etc.
*/
static pj_status_t create_stream( pj_pool_t *pool,
pjmedia_endpt *med_endpt,
const pjmedia_vid_codec_info *codec_info,
pjmedia_vid_codec_param *codec_param,
pjmedia_dir dir,
pj_int8_t rx_pt,
pj_int8_t tx_pt,
pj_uint16_t local_port,
const pj_sockaddr_in *rem_addr,
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
pj_bool_t use_srtp,
const pj_str_t *crypto_suite,
const pj_str_t *srtp_tx_key,
const pj_str_t *srtp_rx_key,
#endif
pjmedia_vid_stream **p_stream )
{
pjmedia_vid_stream_info info;
pjmedia_transport *transport = NULL;
pj_status_t status;
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
pjmedia_transport *srtp_tp = NULL;
#endif
/* Reset stream info. */
pj_bzero(&info, sizeof(info));
/* Initialize stream info formats */
info.type = PJMEDIA_TYPE_VIDEO;
info.dir = dir;
info.codec_info = *codec_info;
info.tx_pt = (tx_pt == -1)? codec_info->pt : tx_pt;
info.rx_pt = (rx_pt == -1)? codec_info->pt : rx_pt;
info.ssrc = pj_rand();
if (codec_param)
info.codec_param = codec_param;
#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
/* Set default RTCP XR enabled/disabled */
info.rtcp_xr_enabled = PJ_TRUE;
#endif
/* Copy remote address */
pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
/* If remote address is not set, set to an arbitrary address
* (otherwise stream will assert).
*/
if (info.rem_addr.addr.sa_family == 0) {
const pj_str_t addr = pj_str("127.0.0.1");
pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
}
/* Create media transport */
status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
0, &transport);
if (status != PJ_SUCCESS)
return status;
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
/* Check if SRTP enabled */
if (use_srtp) {
pjmedia_srtp_crypto tx_plc, rx_plc;
status = pjmedia_transport_srtp_create(med_endpt, transport,
NULL, &srtp_tp);
if (status != PJ_SUCCESS)
return status;
pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
tx_plc.key = *srtp_tx_key;
tx_plc.name = *crypto_suite;
rx_plc.key = *srtp_rx_key;
rx_plc.name = *crypto_suite;
status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
if (status != PJ_SUCCESS)
return status;
transport = srtp_tp;
}
#endif
/* Now that the stream info is initialized, we can create the
* stream.
*/
status = pjmedia_vid_stream_create( med_endpt, pool, &info,
transport,
NULL, p_stream);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Error creating stream", status);
pjmedia_transport_close(transport);
return status;
}
return PJ_SUCCESS;
}
typedef struct play_file_data
{
const char *file_name;
pjmedia_port *play_port;
pjmedia_port *stream_port;
pjmedia_vid_codec *decoder;
pjmedia_port *renderer;
void *read_buf;
pj_size_t read_buf_size;
void *dec_buf;
pj_size_t dec_buf_size;
} play_file_data;
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 = decoder->op->decode(decoder, &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);
}
/*
* usage()
*/
static void usage()
{
puts(desc);
}
/*
* main()
*/
int main(int argc, char *argv[])
{
pj_caching_pool cp;
pjmedia_endpt *med_endpt;
pj_pool_t *pool;
pjmedia_vid_stream *stream = NULL;
pjmedia_port *enc_port, *dec_port;
pj_status_t status;
pjmedia_vid_port *capture=NULL, *renderer=NULL;
pjmedia_vid_port_param vpp;
#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_vid_codec_info *codec_info;
pjmedia_vid_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;
pjmedia_rect_size tx_size = {0};
pj_int8_t rx_pt = -1, tx_pt = -1;
play_file_data play_file = { NULL };
pjmedia_port *play_port = NULL;
pjmedia_vid_codec *play_decoder = NULL;
pjmedia_clock *play_clock = NULL;
enum {
OPT_CODEC = 'c',
OPT_LOCAL_PORT = 'p',
OPT_REMOTE = 'r',
OPT_PLAY_FILE = 'f',
OPT_SEND_RECV = 'b',
OPT_SEND_ONLY = 's',
OPT_RECV_ONLY = 'i',
OPT_SEND_WIDTH = 'W',
OPT_SEND_HEIGHT = 'H',
OPT_RECV_PT = 't',
OPT_SEND_PT = 'T',
#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 },
{ "send-recv", 0, 0, OPT_SEND_RECV },
{ "send-only", 0, 0, OPT_SEND_ONLY },
{ "recv-only", 0, 0, OPT_RECV_ONLY },
{ "send-width", 1, 0, OPT_SEND_WIDTH },
{ "send-height", 1, 0, OPT_SEND_HEIGHT },
{ "recv-pt", 1, 0, OPT_RECV_PT },
{ "send-pt", 1, 0, OPT_SEND_PT },
#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.file_name = 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;
case OPT_SEND_WIDTH:
tx_size.w = (unsigned)atoi(pj_optarg);
break;
case OPT_SEND_HEIGHT:
tx_size.h = (unsigned)atoi(pj_optarg);
break;
case OPT_RECV_PT:
rx_pt = (pj_int8_t)atoi(pj_optarg);
break;
case OPT_SEND_PT:
tx_pt = (pj_int8_t)atoi(pj_optarg);
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,
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,
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.file_name != 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 */
);
/* Init video format manager */
pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
/* Init video converter manager */
pjmedia_converter_mgr_create(pool, NULL);
/* Init video codec manager */
pjmedia_vid_codec_mgr_create(pool, NULL);
/* Init video subsystem */
pjmedia_vid_subsys_init(&cp.factory);
/* Register all supported codecs */
status = init_codecs(&cp.factory);
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);
status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL,
&str_codec_id, &count,
&codec_info, NULL);
if (status != PJ_SUCCESS) {
printf("Error: unable to find codec %s\n", codec_id);
return 1;
}
} else {
static pjmedia_vid_codec_info info[1];
unsigned count = PJ_ARRAY_SIZE(info);
/* Default to first codec */
pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, NULL);
codec_info = &info[0];
}
/* Get codec default param for info */
status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info,
&codec_param);
pj_assert(status == PJ_SUCCESS);
/* Set outgoing video size */
if (tx_size.w && tx_size.h)
codec_param.enc_fmt.det.vid.size = tx_size;
#if DEF_RENDERER_WIDTH && DEF_RENDERER_HEIGHT
/* Set incoming video size */
codec_param.dec_fmt.det.vid.size.w = DEF_RENDERER_WIDTH;
codec_param.dec_fmt.det.vid.size.h = DEF_RENDERER_HEIGHT;
#endif
if (play_file.file_name) {
pjmedia_video_format_detail *file_vfd;
/* Create file player */
status = create_file_player(pool, play_file.file_name, &play_port);
if (status != PJ_SUCCESS)
goto on_exit;
/* Collect format info */
file_vfd = pjmedia_format_get_video_format_detail(&play_port->info.fmt,
PJ_TRUE);
PJ_LOG(2, (THIS_FILE, "Reading video stream %dx%d %c%c%c%c @%.2dfps",
file_vfd->size.w, file_vfd->size.h,
((play_port->info.fmt.id & 0x000000FF) >> 0),
((play_port->info.fmt.id & 0x0000FF00) >> 8),
((play_port->info.fmt.id & 0x00FF0000) >> 16),
((play_port->info.fmt.id & 0xFF000000) >> 24),
file_vfd->fps.num/file_vfd->fps.denum));
/* Allocate file read buffer */
play_file.read_buf_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE;
play_file.read_buf = pj_pool_zalloc(pool, play_file.read_buf_size);
/* Create decoder, if the file and the stream uses different codec */
if (codec_info->fmt_id != (pjmedia_format_id)play_port->info.fmt.id) {
const pjmedia_video_format_info *dec_vfi;
pjmedia_video_apply_fmt_param dec_vafp = {0};
const pjmedia_vid_codec_info *codec_info2;
pjmedia_vid_codec_param codec_param2;
/* Find decoder */
status = pjmedia_vid_codec_mgr_get_codec_info2(NULL,
play_port->info.fmt.id,
&codec_info2);
if (status != PJ_SUCCESS)
goto on_exit;
/* Init decoder */
status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info2,
&play_decoder);
if (status != PJ_SUCCESS)
goto on_exit;
status = play_decoder->op->init(play_decoder, pool);
if (status != PJ_SUCCESS)
goto on_exit;
/* Open decoder */
status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info2,
&codec_param2);
if (status != PJ_SUCCESS)
goto on_exit;
status = play_decoder->op->open(play_decoder, &codec_param2);
if (status != PJ_SUCCESS)
goto on_exit;
/* Get decoder format info and apply param */
dec_vfi = pjmedia_get_video_format_info(NULL,
codec_info2->dec_fmt_id[0]);
if (!dec_vfi || !dec_vfi->apply_fmt) {
status = PJ_ENOTSUP;
goto on_exit;
}
dec_vafp.size = file_vfd->size;
(*dec_vfi->apply_fmt)(dec_vfi, &dec_vafp);
/* Allocate buffer to receive decoder output */
play_file.dec_buf_size = dec_vafp.framebytes;
play_file.dec_buf = pj_pool_zalloc(pool, play_file.dec_buf_size);
}
/* Create player clock */
status = pjmedia_clock_create2(pool, PJMEDIA_PTIME(&file_vfd->fps),
codec_info->clock_rate,
PJMEDIA_CLOCK_NO_HIGHEST_PRIO,
&clock_cb, &play_file, &play_clock);
if (status != PJ_SUCCESS)
goto on_exit;
/* Override stream codec param for encoding direction */
codec_param.enc_fmt.det.vid.size = file_vfd->size;
codec_param.enc_fmt.det.vid.fps = file_vfd->fps;
} else {
pjmedia_vid_port_param_default(&vpp);
/* Set as active for all video devices */
vpp.active = PJ_TRUE;
/* Create video device port. */
if (dir & PJMEDIA_DIR_ENCODING) {
/* Create capture */
status = pjmedia_vid_dev_default_param(
pool,
0,//PJMEDIA_VID_DEFAULT_CAPTURE_DEV,
&vpp.vidparam);
if (status != PJ_SUCCESS)
goto on_exit;
pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt);
vpp.vidparam.dir = PJMEDIA_DIR_CAPTURE;
status = pjmedia_vid_port_create(pool, &vpp, &capture);
if (status != PJ_SUCCESS)
goto on_exit;
}
if (dir & PJMEDIA_DIR_DECODING) {
/* Create renderer */
status = pjmedia_vid_dev_default_param(
pool,
1,//PJMEDIA_VID_DEFAULT_RENDER_DEV,
&vpp.vidparam);
if (status != PJ_SUCCESS)
goto on_exit;
pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt);
vpp.vidparam.dir = PJMEDIA_DIR_RENDER;
vpp.vidparam.disp_size = vpp.vidparam.fmt.det.vid.size;
status = pjmedia_vid_port_create(pool, &vpp, &renderer);
if (status != PJ_SUCCESS)
goto on_exit;
}
}
/* Create stream based on program arguments */
status = create_stream(pool, med_endpt, codec_info, &codec_param,
dir, rx_pt, tx_pt, 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 the port interface of the stream */
status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_ENCODING,
&enc_port);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_DECODING,
&dec_port);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
/* Start streaming */
status = pjmedia_vid_stream_start(stream);
if (status != PJ_SUCCESS)
goto on_exit;
/* Start renderer */
if (renderer) {
status = pjmedia_vid_port_connect(renderer, dec_port, PJ_FALSE);
if (status != PJ_SUCCESS)
goto on_exit;
status = pjmedia_vid_port_start(renderer);
if (status != PJ_SUCCESS)
goto on_exit;
}
/* Start capture */
if (capture) {
status = pjmedia_vid_port_connect(capture, enc_port, PJ_FALSE);
if (status != PJ_SUCCESS)
goto on_exit;
status = pjmedia_vid_port_start(capture);
if (status != PJ_SUCCESS)
goto on_exit;
}
/* Start playing file */
if (play_file.file_name) {
#if HAS_LOCAL_RENDERER_FOR_PLAY_FILE
/* Create local renderer */
pjmedia_vid_port_param_default(&vpp);
vpp.active = PJ_FALSE;
status = pjmedia_vid_dev_default_param(
pool,
1,//PJMEDIA_VID_DEFAULT_RENDER_DEV,
&vpp.vidparam);
if (status != PJ_SUCCESS)
goto on_exit;
vpp.vidparam.dir = PJMEDIA_DIR_RENDER;
pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt);
vpp.vidparam.fmt.det.vid.size = play_port->info.fmt.det.vid.size;
vpp.vidparam.fmt.det.vid.fps = play_port->info.fmt.det.vid.fps;
vpp.vidparam.disp_size = vpp.vidparam.fmt.det.vid.size;
status = pjmedia_vid_port_create(pool, &vpp, &renderer);
if (status != PJ_SUCCESS)
goto on_exit;
status = pjmedia_vid_port_start(renderer);
if (status != PJ_SUCCESS)
goto on_exit;
#endif
/* Init play file data */
play_file.play_port = play_port;
play_file.stream_port = enc_port;
play_file.decoder = play_decoder;
if (renderer) {
play_file.renderer = pjmedia_vid_port_get_passive_port(renderer);
}
status = pjmedia_clock_start(play_clock);
if (status != PJ_SUCCESS)
goto on_exit;
}
/* 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));
if (dir & PJMEDIA_DIR_ENCODING)
PJ_LOG(2, (THIS_FILE, "Sending %dx%d %.*s @%.2dfps",
codec_param.enc_fmt.det.vid.size.w,
codec_param.enc_fmt.det.vid.size.h,
codec_info->encoding_name.slen,
codec_info->encoding_name.ptr,
codec_param.enc_fmt.det.vid.fps.num/
codec_param.enc_fmt.det.vid.fps.denum));
for (;;) {
char tmp[10];
puts("");
puts("Commands:");
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] == 'q')
break;
}
/* Start deinitialization: */
on_exit:
/* Stop and destroy file clock */
if (play_clock) {
pjmedia_clock_stop(play_clock);
pjmedia_clock_destroy(play_clock);
}
/* Destroy file reader/player */
if (play_port)
pjmedia_port_destroy(play_port);
/* Destroy file decoder */
if (play_decoder)
play_decoder->op->close(play_decoder);
/* Destroy video devices */
if (capture)
pjmedia_vid_port_destroy(capture);
if (renderer)
pjmedia_vid_port_destroy(renderer);
/* Destroy stream */
if (stream) {
pjmedia_transport *tp;
tp = pjmedia_vid_stream_get_transport(stream);
pjmedia_vid_stream_destroy(stream);
pjmedia_transport_close(tp);
}
/* Shutdown video subsystem */
pjmedia_vid_subsys_shutdown();
/* 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;
}

File diff suppressed because it is too large Load Diff