summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/include/pjmedia/errno.h5
-rw-r--r--pjmedia/include/pjmedia/event.h24
-rw-r--r--pjmedia/include/pjmedia/vid_codec.h47
-rw-r--r--pjmedia/include/pjmedia/vid_stream.h23
-rw-r--r--pjmedia/src/pjmedia-codec/ffmpeg_codecs.c205
-rw-r--r--pjmedia/src/pjmedia-videodev/colorbar_dev.c2
-rw-r--r--pjmedia/src/pjmedia-videodev/dshow_dev.c2
-rw-r--r--pjmedia/src/pjmedia-videodev/ios_dev.m2
-rw-r--r--pjmedia/src/pjmedia-videodev/qt_dev.m2
-rw-r--r--pjmedia/src/pjmedia-videodev/v4l2_dev.c1
-rw-r--r--pjmedia/src/pjmedia/errno.c1
-rw-r--r--pjmedia/src/pjmedia/vid_codec.c8
-rw-r--r--pjmedia/src/pjmedia/vid_stream.c54
-rw-r--r--pjmedia/src/test/vid_codec_test.c2
14 files changed, 253 insertions, 125 deletions
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 4f3a6c3e..3c27e35f 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -346,6 +346,11 @@ PJ_BEGIN_DECL
* Invalid mode.
*/
#define PJMEDIA_CODEC_EINMODE (PJMEDIA_ERRNO_START+86) /* 220086 */
+/**
+ * @hideinitializer
+ * Bad or corrupted bitstream.
+ */
+#define PJMEDIA_CODEC_EBADBITSTREAM (PJMEDIA_ERRNO_START+87) /* 220087 */
/************************************************************
diff --git a/pjmedia/include/pjmedia/event.h b/pjmedia/include/pjmedia/event.h
index 7a779e79..ba06625c 100644
--- a/pjmedia/include/pjmedia/event.h
+++ b/pjmedia/include/pjmedia/event.h
@@ -70,14 +70,14 @@ typedef enum pjmedia_event_type
PJMEDIA_EVENT_MOUSE_BTN_DOWN = PJMEDIA_FOURCC('M', 'S', 'D', 'N'),
/**
- * Video key frame has just been decoded event.
+ * Video keyframe has just been decoded event.
*/
- PJMEDIA_EVENT_KEY_FRAME_FOUND = PJMEDIA_FOURCC('I', 'F', 'R', 'F'),
+ PJMEDIA_EVENT_KEYFRAME_FOUND = PJMEDIA_FOURCC('I', 'F', 'R', 'F'),
/**
- * Video decoding error due to missing key frame event.
+ * Video decoding error due to missing keyframe event.
*/
- PJMEDIA_EVENT_KEY_FRAME_MISSING = PJMEDIA_FOURCC('I', 'F', 'R', 'M'),
+ PJMEDIA_EVENT_KEYFRAME_MISSING = PJMEDIA_FOURCC('I', 'F', 'R', 'M'),
/**
* Video orientation has been changed event.
@@ -135,11 +135,11 @@ typedef pjmedia_event_dummy_data pjmedia_event_wnd_closed_data;
/** Additional parameters for mouse button down event */
typedef pjmedia_event_dummy_data pjmedia_event_mouse_btn_down_data;
-/** Additional parameters for key frame found event */
-typedef pjmedia_event_dummy_data pjmedia_event_key_frame_found_data;
+/** Additional parameters for keyframe found event */
+typedef pjmedia_event_dummy_data pjmedia_event_keyframe_found_data;
-/** Additional parameters for key frame missing event */
-typedef pjmedia_event_dummy_data pjmedia_event_key_frame_missing_data;
+/** Additional parameters for keyframe missing event */
+typedef pjmedia_event_dummy_data pjmedia_event_keyframe_missing_data;
/**
* Maximum size of additional parameters section in pjmedia_event structure
@@ -205,11 +205,11 @@ typedef struct pjmedia_event
/** Mouse button down event data */
pjmedia_event_mouse_btn_down_data mouse_btn_down;
- /** Key frame found event data */
- pjmedia_event_key_frame_found_data key_frm_found;
+ /** Keyframe found event data */
+ pjmedia_event_keyframe_found_data keyframe_found;
- /** Key frame missing event data */
- pjmedia_event_key_frame_missing_data key_frm_missing;
+ /** Keyframe missing event data */
+ pjmedia_event_keyframe_missing_data keyframe_missing;
/** Storage for user event data */
pjmedia_event_user_data user;
diff --git a/pjmedia/include/pjmedia/vid_codec.h b/pjmedia/include/pjmedia/vid_codec.h
index 9716e540..6029f91e 100644
--- a/pjmedia/include/pjmedia/vid_codec.h
+++ b/pjmedia/include/pjmedia/vid_codec.h
@@ -76,6 +76,37 @@ typedef enum pjmedia_vid_packing
} pjmedia_vid_packing;
+
+/**
+ * Enumeration of video frame info flag for the bit_info field in the
+ * pjmedia_frame.
+ */
+typedef enum pjmedia_vid_frm_bit_info
+{
+ /**
+ * The video frame is keyframe.
+ */
+ PJMEDIA_VID_FRM_KEYFRAME = 1
+
+} pjmedia_vid_frm_bit_info;
+
+
+/**
+ * Encoding option.
+ */
+typedef struct pjmedia_vid_encode_opt
+{
+ /**
+ * Flag to force the encoder to generate keyframe for the specified input
+ * frame. When this flag is set, application can verify the result by
+ * examining PJMEDIA_VID_FRM_KEYFRAME flag in the bit_info field of the
+ * output frame.
+ */
+ pj_bool_t force_keyframe;
+
+} pjmedia_vid_encode_opt;
+
+
/**
* Identification used to search for codec factory that supports specific
* codec specification.
@@ -178,7 +209,7 @@ typedef struct pjmedia_vid_codec_op
/**
* See #pjmedia_vid_codec_modify().
*/
- pj_status_t (*modify)(pjmedia_vid_codec *codec,
+ pj_status_t (*modify)(pjmedia_vid_codec *codec,
const pjmedia_vid_codec_param *param);
/**
@@ -191,6 +222,7 @@ typedef struct pjmedia_vid_codec_op
* See #pjmedia_vid_codec_encode_begin().
*/
pj_status_t (*encode_begin)(pjmedia_vid_codec *codec,
+ const pjmedia_vid_encode_opt *opt,
const pjmedia_frame *input,
unsigned out_size,
pjmedia_frame *output,
@@ -363,15 +395,6 @@ typedef struct pjmedia_vid_codec_mgr pjmedia_vid_codec_mgr;
/**
- * Initialize pjmedia_vid_codec structure with default values.
- *
- * @param codec The codec to be initialized.
- * @param sig Codec's object signature (see signatures.h)
- */
-PJ_DECL(void) pjmedia_vid_codec_reset(pjmedia_vid_codec *codec,
- pjmedia_obj_sig sig);
-
-/**
* Initialize codec manager. If there is no the default video codec manager,
* this function will automatically set the default video codec manager to
* the new codec manager instance. Normally this function is called by pjmedia
@@ -728,6 +751,7 @@ pjmedia_vid_codec_get_param(pjmedia_vid_codec *codec,
* codec.
*
* @param codec The codec instance.
+ * @param opt Optional encoding options.
* @param input The input frame.
* @param out_size The length of buffer in the output frame. This
* should be at least the same as the configured
@@ -741,12 +765,13 @@ pjmedia_vid_codec_get_param(pjmedia_vid_codec *codec,
*/
PJ_INLINE(pj_status_t)
pjmedia_vid_codec_encode_begin( pjmedia_vid_codec *codec,
+ const pjmedia_vid_encode_opt *opt,
const pjmedia_frame *input,
unsigned out_size,
pjmedia_frame *output,
pj_bool_t *has_more)
{
- return (*codec->op->encode_begin)(codec, input, out_size, output,
+ return (*codec->op->encode_begin)(codec, opt, input, out_size, output,
has_more);
}
diff --git a/pjmedia/include/pjmedia/vid_stream.h b/pjmedia/include/pjmedia/vid_stream.h
index bdd84079..4ce819bd 100644
--- a/pjmedia/include/pjmedia/vid_stream.h
+++ b/pjmedia/include/pjmedia/vid_stream.h
@@ -298,10 +298,10 @@ PJ_DECL(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream,
pjmedia_dir dir);
/**
- * Pause the individual channel in the stream.
+ * Pause stream channels.
*
- * @param stream The video channel.
- * @param dir Which direction to pause.
+ * @param stream The video stream.
+ * @param dir Which channel direction to pause.
*
* @return PJ_SUCCESS on success.
*/
@@ -309,10 +309,10 @@ PJ_DECL(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
pjmedia_dir dir);
/**
- * Resume the individual channel in the stream.
+ * Resume stream channels.
*
- * @param stream The video channel.
- * @param dir Which direction to resume.
+ * @param stream The video stream.
+ * @param dir Which channel direction to resume.
*
* @return PJ_SUCCESS on success;
*/
@@ -321,6 +321,17 @@ PJ_DECL(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
/**
+ * Force stream to send video keyframe on the next transmission.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_send_keyframe(
+ pjmedia_vid_stream *stream);
+
+
+/**
* @}
*/
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
index ccd0901f..96b77be1 100644
--- a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
@@ -77,11 +77,12 @@ static pj_status_t ffmpeg_codec_modify(pjmedia_vid_codec *codec,
const pjmedia_vid_codec_param *attr );
static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
pjmedia_vid_codec_param *param);
-static pj_status_t ffmpeg_codec_encode_begin( pjmedia_vid_codec *codec,
- const pjmedia_frame *input,
- unsigned out_size,
- pjmedia_frame *output,
- pj_bool_t *has_more);
+static pj_status_t ffmpeg_codec_encode_begin(pjmedia_vid_codec *codec,
+ const pjmedia_vid_encode_opt *opt,
+ const pjmedia_frame *input,
+ unsigned out_size,
+ pjmedia_frame *output,
+ pj_bool_t *has_more);
static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec,
unsigned out_size,
pjmedia_frame *output,
@@ -136,8 +137,6 @@ typedef struct ffmpeg_private
const ffmpeg_codec_desc *desc;
pjmedia_vid_codec_param param; /**< Codec param */
pj_pool_t *pool; /**< Pool for each instance */
- pj_timestamp last_tx; /**< Timestamp of last
- transmit */
/* Format info and apply format param */
const pjmedia_video_format_info *enc_vfi;
@@ -149,10 +148,12 @@ typedef struct ffmpeg_private
pj_bool_t whole;
void *enc_buf;
unsigned enc_buf_size;
+ pj_bool_t enc_buf_is_keyframe;
unsigned enc_frame_len;
unsigned enc_processed;
void *dec_buf;
unsigned dec_buf_size;
+ pj_timestamp last_dec_keyframe_ts;
/* The ffmpeg codec states. */
AVCodec *enc;
@@ -981,12 +982,11 @@ static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory,
/* Create pool for codec instance */
pool = pj_pool_create(ffmpeg_factory.pf, "ffmpeg codec", 512, 512, NULL);
- codec = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec);
+ codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec);
if (!codec) {
status = PJ_ENOMEM;
goto on_error;
}
- pjmedia_vid_codec_reset(codec, PJMEDIA_SIG_VID_CODEC_FFMPEG);
codec->op = &ffmpeg_op;
codec->factory = factory;
ff = PJ_POOL_ZALLOC_T(pool, ffmpeg_private);
@@ -1361,6 +1361,7 @@ static pj_status_t ffmpeg_unpacketize(pjmedia_vid_codec *codec,
* Encode frames.
*/
static pj_status_t ffmpeg_codec_encode_whole(pjmedia_vid_codec *codec,
+ const pjmedia_vid_encode_opt *opt,
const pjmedia_frame *input,
unsigned output_buf_len,
pjmedia_frame *output)
@@ -1401,22 +1402,30 @@ static pj_status_t ffmpeg_codec_encode_whole(pjmedia_vid_codec *codec,
p += ff->enc_vafp.plane_bytes[i[0]];
}
+ /* Force keyframe */
+ if (opt && opt->force_keyframe)
+ avframe.pict_type = AV_PICTURE_TYPE_I;
+
err = avcodec_encode_video(ff->enc_ctx, out_buf, out_buf_len, &avframe);
if (err < 0) {
print_ffmpeg_err(err);
return PJMEDIA_CODEC_EFAILED;
} else {
output->size = err;
+ output->bit_info = 0;
+ if (ff->enc_ctx->coded_frame->key_frame)
+ output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
}
return PJ_SUCCESS;
}
-static pj_status_t ffmpeg_codec_encode_begin( pjmedia_vid_codec *codec,
- const pjmedia_frame *input,
- unsigned out_size,
- pjmedia_frame *output,
- pj_bool_t *has_more)
+static pj_status_t ffmpeg_codec_encode_begin(pjmedia_vid_codec *codec,
+ const pjmedia_vid_encode_opt *opt,
+ const pjmedia_frame *input,
+ unsigned out_size,
+ pjmedia_frame *output,
+ pj_bool_t *has_more)
{
ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
pj_status_t status;
@@ -1424,7 +1433,8 @@ static pj_status_t ffmpeg_codec_encode_begin( pjmedia_vid_codec *codec,
*has_more = PJ_FALSE;
if (ff->whole) {
- status = ffmpeg_codec_encode_whole(codec, input, out_size, output);
+ status = ffmpeg_codec_encode_whole(codec, opt, input, out_size,
+ output);
} else {
pjmedia_frame whole_frm;
const pj_uint8_t *payload;
@@ -1433,11 +1443,13 @@ static pj_status_t ffmpeg_codec_encode_begin( pjmedia_vid_codec *codec,
pj_bzero(&whole_frm, sizeof(whole_frm));
whole_frm.buf = ff->enc_buf;
whole_frm.size = ff->enc_buf_size;
- status = ffmpeg_codec_encode_whole(codec, input,
+ status = ffmpeg_codec_encode_whole(codec, opt, input,
whole_frm.size, &whole_frm);
if (status != PJ_SUCCESS)
return status;
+ ff->enc_buf_is_keyframe = (whole_frm.bit_info &
+ PJMEDIA_VID_FRM_KEYFRAME);
ff->enc_frame_len = (unsigned)whole_frm.size;
ff->enc_processed = 0;
status = ffmpeg_packetize(codec, (pj_uint8_t*)whole_frm.buf,
@@ -1453,6 +1465,9 @@ static pj_status_t ffmpeg_codec_encode_begin( pjmedia_vid_codec *codec,
pj_memcpy(output->buf, payload, payload_len);
output->size = payload_len;
+ if (ff->enc_buf_is_keyframe)
+ output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+
*has_more = (ff->enc_processed < ff->enc_frame_len);
}
@@ -1489,12 +1504,90 @@ static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec,
pj_memcpy(output->buf, payload, payload_len);
output->size = payload_len;
+ if (ff->enc_buf_is_keyframe)
+ output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+
*has_more = (ff->enc_processed < ff->enc_frame_len);
return PJ_SUCCESS;
}
+static pj_status_t check_decode_result(pjmedia_vid_codec *codec,
+ const pj_timestamp *ts,
+ pj_bool_t got_keyframe)
+{
+ ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+ pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
+ pjmedia_event event;
+
+ /* Check for format change.
+ * Decoder output format is set by libavcodec, in case it is different
+ * to the configured param.
+ */
+ if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt ||
+ ff->dec_ctx->width != (int)vafp->size.w ||
+ ff->dec_ctx->height != (int)vafp->size.h)
+ {
+ pjmedia_format_id new_fmt_id;
+ pj_status_t status;
+
+ /* Get current raw format id from ffmpeg decoder context */
+ status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt,
+ &new_fmt_id);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Update decoder format in param */
+ ff->param.dec_fmt.id = new_fmt_id;
+ ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->width;
+ ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->height;
+ ff->expected_dec_fmt = ff->dec_ctx->pix_fmt;
+
+ /* Re-init format info and apply-param of decoder */
+ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->dec_vfi)
+ return PJ_ENOTSUP;
+ pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+ ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+ ff->dec_vafp.buffer = NULL;
+ status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Realloc buffer if necessary */
+ if (ff->dec_vafp.framebytes > ff->dec_buf_size) {
+ PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u",
+ (unsigned)ff->dec_buf_size,
+ (unsigned)ff->dec_vafp.framebytes));
+ ff->dec_buf_size = ff->dec_vafp.framebytes;
+ ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size);
+ }
+
+ /* Broadcast format changed event */
+ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, ts, codec);
+ event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
+ pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt,
+ sizeof(ff->param.dec_fmt));
+ pjmedia_event_publish(NULL, codec, &event, 0);
+ }
+
+ /* Check for missing/found keyframe */
+ if (got_keyframe) {
+ pj_get_timestamp(&ff->last_dec_keyframe_ts);
+
+ /* Broadcast keyframe event */
+ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_FOUND, ts, codec);
+ pjmedia_event_publish(NULL, codec, &event, 0);
+ } else if (ff->last_dec_keyframe_ts.u64 == 0) {
+ /* Broadcast missing keyframe event */
+ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, ts, codec);
+ pjmedia_event_publish(NULL, codec, &event, 0);
+ }
+
+ return PJ_SUCCESS;
+}
+
/*
* Decode frame.
*/
@@ -1557,69 +1650,31 @@ static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec,
&got_picture, avpacket.data, avpacket.size);
#endif
if (err < 0) {
+ pjmedia_event event;
+
output->type = PJMEDIA_FRAME_TYPE_NONE;
output->size = 0;
print_ffmpeg_err(err);
- return PJMEDIA_CODEC_EFAILED;
+
+ /* Broadcast missing keyframe event */
+ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING,
+ &input->timestamp, codec);
+ pjmedia_event_publish(NULL, codec, &event, 0);
+
+ return PJMEDIA_CODEC_EBADBITSTREAM;
} else if (got_picture) {
pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
pj_uint8_t *q = (pj_uint8_t*)output->buf;
unsigned i;
+ pj_status_t status;
- /* Decoder output format is set by libavcodec, in case it is different
- * to the configured param.
+ /* Check decoding result, e.g: see if the format got changed,
+ * keyframe found/missing.
*/
- if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt ||
- ff->dec_ctx->width != (int)vafp->size.w ||
- ff->dec_ctx->height != (int)vafp->size.h)
- {
- pjmedia_format_id new_fmt_id;
- pj_status_t status;
-
- /* Get current raw format id from ffmpeg decoder context */
- status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt,
- &new_fmt_id);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Update decoder format in param */
- ff->param.dec_fmt.id = new_fmt_id;
- ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->width;
- ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->height;
- ff->expected_dec_fmt = ff->dec_ctx->pix_fmt;
-
- /* Re-init format info and apply-param of decoder */
- ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
- if (!ff->dec_vfi)
- return PJ_ENOTSUP;
- pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
- ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
- ff->dec_vafp.buffer = NULL;
- status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Realloc buffer if necessary */
- if (ff->dec_vafp.framebytes > ff->dec_buf_size) {
- PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u",
- (unsigned)ff->dec_buf_size,
- (unsigned)ff->dec_vafp.framebytes));
- ff->dec_buf_size = ff->dec_vafp.framebytes;
- ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size);
- }
-
- /* Broadcast event */
- {
- pjmedia_event event;
-
- pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED,
- &input->timestamp, codec);
- event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
- pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt,
- sizeof(ff->param.dec_fmt));
- pjmedia_event_publish(NULL, codec, &event, 0);
- }
- }
+ status = check_decode_result(codec, &input->timestamp,
+ avframe.key_frame);
+ if (status != PJ_SUCCESS)
+ return status;
/* Check provided buffer size */
if (vafp->framebytes > output_buf_len)
@@ -1649,16 +1704,6 @@ static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec,
output->type = PJMEDIA_FRAME_TYPE_VIDEO;
output->size = vafp->framebytes;
-
- /* Check if we got key frame */
- if (avframe.key_frame)
- {
- pjmedia_event event;
-
- pjmedia_event_init(&event, PJMEDIA_EVENT_KEY_FRAME_FOUND,
- &output->timestamp, codec);
- pjmedia_event_publish(NULL, codec, &event, 0);
- }
} else {
output->type = PJMEDIA_FRAME_TYPE_NONE;
output->size = 0;
diff --git a/pjmedia/src/pjmedia-videodev/colorbar_dev.c b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
index 6fb658ef..11acf6cb 100644
--- a/pjmedia/src/pjmedia-videodev/colorbar_dev.c
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -577,6 +577,8 @@ static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
{
struct cbar_stream *stream = (struct cbar_stream*)strm;
+ frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
+ frame->bit_info = 0;
frame->timestamp = stream->ts;
stream->ts.u64 += stream->ts_inc;
return spectrum_run(stream, frame->buf, frame->size);
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
index c116875f..875a0e89 100644
--- a/pjmedia/src/pjmedia-videodev/dshow_dev.c
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -577,7 +577,7 @@ static void input_cb(void *user_data, IMediaSample *pMediaSample)
PJ_LOG(5,(THIS_FILE, "Capture thread started"));
}
- frame.type = PJMEDIA_TYPE_VIDEO;
+ frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
frame.size = IMediaSample_GetActualDataLength(pMediaSample);
frame.bit_info = 0;
diff --git a/pjmedia/src/pjmedia-videodev/ios_dev.m b/pjmedia/src/pjmedia-videodev/ios_dev.m
index 93b165f1..d81be142 100644
--- a/pjmedia/src/pjmedia-videodev/ios_dev.m
+++ b/pjmedia/src/pjmedia-videodev/ios_dev.m
@@ -360,7 +360,7 @@ static pj_status_t ios_factory_default_param(pj_pool_t *pool,
/* Lock the base address of the pixel buffer */
CVPixelBufferLockBaseAddress(imageBuffer, 0);
- frame.type = PJMEDIA_TYPE_VIDEO;
+ frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
frame.buf = CVPixelBufferGetBaseAddress(imageBuffer);
frame.size = stream->frame_size;
frame.bit_info = 0;
diff --git a/pjmedia/src/pjmedia-videodev/qt_dev.m b/pjmedia/src/pjmedia-videodev/qt_dev.m
index 8c098fed..39b769d9 100644
--- a/pjmedia/src/pjmedia-videodev/qt_dev.m
+++ b/pjmedia/src/pjmedia-videodev/qt_dev.m
@@ -371,7 +371,7 @@ static qt_fmt_info* get_qt_format_info(pjmedia_format_id id)
if (!videoFrame)
return;
- frame.type = PJMEDIA_TYPE_VIDEO;
+ frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
frame.buf = [sampleBuffer bytesForAllSamples];
frame.size = size;
frame.bit_info = 0;
diff --git a/pjmedia/src/pjmedia-videodev/v4l2_dev.c b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
index 9e67234f..260799ff 100644
--- a/pjmedia/src/pjmedia-videodev/v4l2_dev.c
+++ b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
@@ -690,6 +690,7 @@ static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
PJ_TIME_VAL_SUB(time, stream->start_time);
frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
+ frame->bit_info = 0;
frame->size = buf.bytesused;
frame->timestamp.u64 = PJ_UINT64(1) * PJ_TIME_VAL_MSEC(time) *
stream->param.clock_rate / PJ_UINT64(1000);
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index db66a398..788f7431 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -104,6 +104,7 @@ static const struct
PJ_BUILD_ERR( PJMEDIA_CODEC_EFRMINLEN, "Invalid codec frame length" ),
PJ_BUILD_ERR( PJMEDIA_CODEC_EPCMFRMINLEN, "Invalid PCM frame length" ),
PJ_BUILD_ERR( PJMEDIA_CODEC_EINMODE, "Invalid codec mode (no fmtp?)" ),
+ PJ_BUILD_ERR( PJMEDIA_CODEC_EBADBITSTREAM, "Bad or corrupted bitstream" ),
/* Media errors. */
PJ_BUILD_ERR( PJMEDIA_EINVALIDIP, "Invalid remote media (IP) address" ),
diff --git a/pjmedia/src/pjmedia/vid_codec.c b/pjmedia/src/pjmedia/vid_codec.c
index 2002c6d5..07387991 100644
--- a/pjmedia/src/pjmedia/vid_codec.c
+++ b/pjmedia/src/pjmedia/vid_codec.c
@@ -70,14 +70,6 @@ struct pjmedia_vid_codec_mgr
/* Sort codecs in codec manager based on priorities */
static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
-/*
- * Initialize pjmedia_vid_codec structure with default values.
- */
-PJ_DEF(void) pjmedia_vid_codec_reset(pjmedia_vid_codec *codec,
- pjmedia_obj_sig sig)
-{
- pj_bzero(codec, sizeof(*codec));
-}
/*
* Duplicate video codec parameter.
diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c
index 91cedf3d..8d055770 100644
--- a/pjmedia/src/pjmedia/vid_stream.c
+++ b/pjmedia/src/pjmedia/vid_stream.c
@@ -68,6 +68,7 @@
# define PJMEDIA_VSTREAM_INC 1000
#endif
+
/**
* Media channel.
*/
@@ -123,6 +124,9 @@ struct pjmedia_vid_stream
pjmedia_frame dec_frame; /**< Current decoded frame. */
pjmedia_event fmt_event; /**< Buffered fmt_changed event
to avoid deadlock */
+ pjmedia_event miss_keyframe_event;
+ /**< Buffered missing keyframe
+ event for delayed republish*/
unsigned frame_size; /**< Size of encoded base frame.*/
unsigned frame_ts_len; /**< Frame length in timestamp. */
@@ -131,6 +135,8 @@ struct pjmedia_vid_stream
pjmedia_frame *rx_frames; /**< Temp. buffer for incoming
frame assembly. */
+ pj_bool_t force_keyframe;/**< Forced to encode keyframe? */
+
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
pj_bool_t use_ka; /**< Stream keep-alive with non-
codec-VAD mechanism is
@@ -351,6 +357,12 @@ static pj_status_t stream_event_cb(pjmedia_event *event,
*/
pj_memcpy(&stream->fmt_event, event, sizeof(*event));
return PJ_SUCCESS;
+
+ case PJMEDIA_EVENT_KEYFRAME_MISSING:
+ /* Republish this event later from get_frame(). */
+ pj_memcpy(&stream->miss_keyframe_event, event, sizeof(*event));
+ return PJ_SUCCESS;
+
default:
break;
}
@@ -763,7 +775,7 @@ static pj_status_t put_frame(pjmedia_port *port,
int rtphdrlen;
pj_bool_t has_more_data = PJ_FALSE;
pj_size_t total_sent = 0;
-
+ pjmedia_vid_encode_opt enc_opt;
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
/* If the interval since last sending packet is greater than
@@ -796,8 +808,18 @@ static pj_status_t put_frame(pjmedia_port *port,
frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr);
frame_out.size = 0;
+ /* Init encoding option */
+ pj_bzero(&enc_opt, sizeof(enc_opt));
+ if (stream->force_keyframe) {
+ /* Force encoder to generate keyframe */
+ enc_opt.force_keyframe = PJ_TRUE;
+ stream->force_keyframe = PJ_FALSE;
+ TRC_((channel->port.info.name.ptr,
+ "Forcing encoder to generate keyframe"));
+ }
+
/* Encode! */
- status = pjmedia_vid_codec_encode_begin(stream->codec, frame,
+ status = pjmedia_vid_codec_encode_begin(stream->codec, &enc_opt, frame,
channel->buf_size -
sizeof(pjmedia_rtp_hdr),
&frame_out,
@@ -1079,6 +1101,12 @@ static pj_status_t get_frame(pjmedia_port *port,
stream->fmt_event.type = PJMEDIA_EVENT_NONE;
}
+ if (stream->miss_keyframe_event.type != PJMEDIA_EVENT_NONE) {
+ pjmedia_event_publish(NULL, port, &stream->miss_keyframe_event,
+ PJMEDIA_EVENT_PUBLISH_POST_EVENT);
+ stream->miss_keyframe_event.type = PJMEDIA_EVENT_NONE;
+ }
+
pj_mutex_lock( stream->jb_mutex );
if (stream->dec_frame.size == 0) {
@@ -1424,8 +1452,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
/* Set up jitter buffer */
- pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);
- //pjmedia_jbuf_enable_discard(stream->jb, PJ_FALSE);
+ pjmedia_jbuf_set_adaptive(stream->jb, jb_init, jb_min_pre, jb_max_pre);
+ pjmedia_jbuf_set_discard(stream->jb, PJMEDIA_JB_DISCARD_NONE);
/* Init RTCP session: */
{
@@ -2090,4 +2118,22 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
return status;
}
+
+/*
+ * Force stream to send video keyframe.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_send_keyframe(
+ pjmedia_vid_stream *stream)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if (!pjmedia_vid_stream_is_running(stream, PJMEDIA_DIR_ENCODING))
+ return PJ_EINVALIDOP;
+
+ stream->force_keyframe = PJ_TRUE;
+
+ return PJ_SUCCESS;
+}
+
+
#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c
index 11ef93ab..3df532ab 100644
--- a/pjmedia/src/test/vid_codec_test.c
+++ b/pjmedia/src/test/vid_codec_test.c
@@ -94,7 +94,7 @@ static pj_status_t codec_put_frame(pjmedia_port *port,
enc_frames[enc_cnt].buf = enc_buf;
enc_frames[enc_cnt].size = enc_size_left;
- status = pjmedia_vid_codec_encode_begin(codec, frame, 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;