Index: pjproject/trunk/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c	(revision 3734)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c	(revision 3776)
@@ -71,24 +71,18 @@
 static pj_status_t  ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
 					   pjmedia_vid_codec_param *param);
-static pj_status_t  ffmpeg_packetize ( pjmedia_vid_codec *codec,
-                                       pj_uint8_t *buf,
-                                       pj_size_t buf_len,
-                                       unsigned *pos,
-                                       const pj_uint8_t **payload,
-                                       pj_size_t *payload_len);
-static pj_status_t  ffmpeg_unpacketize(pjmedia_vid_codec *codec,
-                                       const pj_uint8_t *payload,
-                                       pj_size_t   payload_len,
-                                       pj_uint8_t *buf,
-                                       pj_size_t   buf_len,
-				       unsigned	  *pos);
-static pj_status_t  ffmpeg_codec_encode( pjmedia_vid_codec *codec, 
-				         const pjmedia_frame *input,
-				         unsigned output_buf_len, 
-				         pjmedia_frame *output);
-static pj_status_t  ffmpeg_codec_decode( pjmedia_vid_codec *codec, 
-				         const pjmedia_frame *input,
-				         unsigned output_buf_len, 
-				         pjmedia_frame *output);
+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_more(pjmedia_vid_codec *codec,
+					    unsigned out_size,
+					    pjmedia_frame *output,
+					    pj_bool_t *has_more);
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
+					pj_size_t pkt_count,
+					pjmedia_frame packets[],
+					unsigned out_size,
+					pjmedia_frame *output);
 
 /* Definition for FFMPEG codecs operations. */
@@ -100,7 +94,6 @@
     &ffmpeg_codec_modify,
     &ffmpeg_codec_get_param,
-    &ffmpeg_packetize,
-    &ffmpeg_unpacketize,
-    &ffmpeg_codec_encode,
+    &ffmpeg_codec_encode_begin,
+    &ffmpeg_codec_encode_more,
     &ffmpeg_codec_decode,
     NULL
@@ -145,4 +138,13 @@
     const pjmedia_video_format_info *dec_vfi;
     pjmedia_video_apply_fmt_param    dec_vafp;
+
+    /* Buffers, only needed for multi-packets */
+    pj_bool_t			     whole;
+    void			    *enc_buf;
+    unsigned			     enc_buf_size;
+    unsigned			     enc_frame_len;
+    unsigned     		     enc_processed;
+    void			    *dec_buf;
+    unsigned			     dec_buf_size;
 
     /* The ffmpeg codec states. */
@@ -236,5 +238,5 @@
 
 /* Internal codec info */
-ffmpeg_codec_desc codec_desc[] =
+static ffmpeg_codec_desc codec_desc[] =
 {
 #if ENABLE_H264
@@ -478,5 +480,6 @@
 	    (desc->info.fmt_id == info->fmt_id) &&
             ((desc->info.dir & info->dir) == info->dir) &&
-	    (desc->info.pt == info->pt))
+	    (desc->info.pt == info->pt) &&
+	    (desc->info.packings & info->packings))
         {
             return desc;
@@ -540,6 +543,5 @@
 
     /* Enum FFMPEG codecs */
-    for (c=av_codec_next(NULL); c; c=av_codec_next(c)) 
-    {
+    for (c=av_codec_next(NULL); c; c=av_codec_next(c)) {
         ffmpeg_codec_desc *desc;
 	pjmedia_format_id fmt_id;
@@ -659,7 +661,9 @@
 	    desc->info.clock_rate = 90000;
 
-	/* Set RTP packetization support flag in the codec info */
-	desc->info.has_rtp_pack = (desc->packetize != NULL) &&
-				  (desc->unpacketize != NULL);
+	/* Set supported packings */
+	desc->info.packings |= PJMEDIA_VID_PACKING_WHOLE;
+	if (desc->packetize && desc->unpacketize)
+	    desc->info.packings |= PJMEDIA_VID_PACKING_PACKETS;
+
     }
 
@@ -707,6 +711,9 @@
 	    desc->info.dir |= copied_dir;
 	    desc->enabled = (desc->info.dir != PJMEDIA_DIR_NONE);
-	    desc->info.has_rtp_pack = (desc->packetize != NULL) &&
-				      (desc->unpacketize != NULL);
+
+	    /* Set supported packings */
+	    desc->info.packings |= PJMEDIA_VID_PACKING_WHOLE;
+	    if (desc->packetize && desc->unpacketize)
+		desc->info.packings |= PJMEDIA_VID_PACKING_PACKETS;
 
 	    if (copied_dir != PJMEDIA_DIR_NONE) {
@@ -802,4 +809,5 @@
 {
     const ffmpeg_codec_desc *desc;
+    unsigned i;
 
     PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
@@ -812,4 +820,18 @@
 
     pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
+
+    /* Scan the requested packings and use the lowest number */
+    attr->packing = 0;
+    for (i=0; i<15; ++i) {
+	unsigned packing = (1 << i);
+	if ((desc->info.packings & info->packings) & packing) {
+	    attr->packing = (pjmedia_vid_packing)packing;
+	    break;
+	}
+    }
+    if (attr->packing == 0) {
+	/* No supported packing in info */
+	return PJMEDIA_CODEC_EUNSUP;
+    }
 
     /* Direction */
@@ -1153,4 +1175,14 @@
     }
 
+    /* Alloc buffers if needed */
+    ff->whole = (ff->param.packing == PJMEDIA_VID_PACKING_WHOLE);
+    if (!ff->whole) {
+	ff->enc_buf_size = ff->enc_vafp.framebytes;
+	ff->enc_buf = pj_pool_alloc(ff->pool, ff->enc_buf_size);
+
+	ff->dec_buf_size = ff->dec_vafp.framebytes;
+	ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size);
+    }
+
     /* Update codec attributes, e.g: encoding format may be changed by
      * SDP fmtp negotiation.
@@ -1260,8 +1292,8 @@
  * Encode frames.
  */
-static pj_status_t ffmpeg_codec_encode( pjmedia_vid_codec *codec, 
-				        const pjmedia_frame *input,
-				        unsigned output_buf_len, 
-				        pjmedia_frame *output)
+static pj_status_t ffmpeg_codec_encode_whole(pjmedia_vid_codec *codec,
+					     const pjmedia_frame *input,
+					     unsigned output_buf_len,
+					     pjmedia_frame *output)
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
@@ -1312,11 +1344,94 @@
 }
 
+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)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pj_status_t status;
+
+    *has_more = PJ_FALSE;
+
+    if (ff->whole) {
+	status = ffmpeg_codec_encode_whole(codec, input, out_size, output);
+    } else {
+	pjmedia_frame whole_frm;
+        const pj_uint8_t *payload;
+        pj_size_t payload_len;
+
+	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,
+	                                   whole_frm.size, &whole_frm);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	ff->enc_frame_len = (unsigned)whole_frm.size;
+	ff->enc_processed = 0;
+        status = ffmpeg_packetize(codec, (pj_uint8_t*)whole_frm.buf,
+                                  whole_frm.size, &ff->enc_processed,
+				  &payload, &payload_len);
+        if (status != PJ_SUCCESS)
+            return status;
+
+        if (out_size < payload_len)
+            return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+        output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+        pj_memcpy(output->buf, payload, payload_len);
+        output->size = payload_len;
+
+        *has_more = (ff->enc_processed < ff->enc_frame_len);
+    }
+
+    return status;
+}
+
+static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec,
+					    unsigned out_size,
+					    pjmedia_frame *output,
+					    pj_bool_t *has_more)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    const pj_uint8_t *payload;
+    pj_size_t payload_len;
+    pj_status_t status;
+
+    *has_more = PJ_FALSE;
+
+    if (ff->enc_processed >= ff->enc_frame_len) {
+	/* No more frame */
+	return PJ_EEOF;
+    }
+
+    status = ffmpeg_packetize(codec, (pj_uint8_t*)ff->enc_buf,
+                              ff->enc_frame_len, &ff->enc_processed,
+                              &payload, &payload_len);
+    if (status != PJ_SUCCESS)
+        return status;
+
+    if (out_size < payload_len)
+        return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+    output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    pj_memcpy(output->buf, payload, payload_len);
+    output->size = payload_len;
+
+    *has_more = (ff->enc_processed < ff->enc_frame_len);
+
+    return PJ_SUCCESS;
+}
+
+
 /*
  * Decode frame.
  */
-static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec, 
-				        const pjmedia_frame *input,
-				        unsigned output_buf_len, 
-				        pjmedia_frame *output)
+static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec,
+					     const pjmedia_frame *input,
+					     unsigned output_buf_len,
+					     pjmedia_frame *output)
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
@@ -1417,4 +1532,13 @@
 		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 */
 	    if (pjmedia_event_publisher_has_sub(&codec->epub)) {
@@ -1476,4 +1600,48 @@
 }
 
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
+					pj_size_t pkt_count,
+					pjmedia_frame packets[],
+					unsigned out_size,
+					pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output,
+                     PJ_EINVAL);
+
+    if (ff->whole) {
+	pj_assert(pkt_count==1);
+	return ffmpeg_codec_decode_whole(codec, &packets[0], out_size, output);
+    } else {
+	pjmedia_frame whole_frm;
+	unsigned whole_len = 0;
+	unsigned i;
+
+	for (i=0; i<pkt_count; ++i) {
+	    if (whole_len + packets[i].size > ff->dec_buf_size) {
+		PJ_LOG(5,(THIS_FILE, "Decoding buffer overflow"));
+		break;
+	    }
+
+	    status = ffmpeg_unpacketize(codec, packets[i].buf, packets[i].size,
+	                                ff->dec_buf, ff->dec_buf_size,
+	                                &whole_len);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(5,(THIS_FILE, status, "Unpacketize error"));
+		continue;
+	    }
+	}
+
+	whole_frm.buf = ff->dec_buf;
+	whole_frm.size = whole_len;
+	whole_frm.timestamp = output->timestamp = packets[i].timestamp;
+	whole_frm.bit_info = 0;
+
+	return ffmpeg_codec_decode_whole(codec, &whole_frm, out_size, output);
+    }
+}
+
 
 #ifdef _MSC_VER
